mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
refactor: order object position and field service (#4118)
* refactor: create OrderObjectPositionPB * refactor: use ObjectOrderPosition for creating rows * refactor: field backend service * refactor: use field_id for reordering fields * style: reorder dependencies * fix: changes on tauri * feat: insert row above * chore: don't pass group_id while duplicating a row
This commit is contained in:
parent
7586930ef0
commit
4e6643eca8
@ -173,7 +173,6 @@ class _MobileBoardContentState extends State<MobileBoardContent> {
|
||||
rowCache: rowCache,
|
||||
cardData: groupData.group.groupId,
|
||||
groupingFieldId: groupItem.fieldInfo.id,
|
||||
groupId: groupData.group.groupId,
|
||||
isEditing: isEditing,
|
||||
cellBuilder: cellBuilder,
|
||||
renderHook: renderHook,
|
||||
|
@ -10,7 +10,6 @@ import 'package:appflowy/mobile/presentation/database/card/card_detail/widgets/w
|
||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_service.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/type_option/number_format_bloc.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_service.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/type_option/date.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart';
|
||||
import 'package:appflowy/util/field_type_extension.dart';
|
||||
@ -56,7 +55,7 @@ class FieldOptionValues {
|
||||
Future<void> create({
|
||||
required String viewId,
|
||||
}) async {
|
||||
await TypeOptionBackendService.createFieldTypeOption(
|
||||
await FieldBackendService.createField(
|
||||
viewId: viewId,
|
||||
fieldType: type,
|
||||
fieldName: name,
|
||||
|
@ -4,7 +4,6 @@ import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/board_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/group_changeset.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart';
|
||||
@ -15,14 +14,13 @@ import 'package:collection/collection.dart';
|
||||
import 'dart:async';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'database_view_service.dart';
|
||||
import 'defines.dart';
|
||||
import 'field/field_info.dart';
|
||||
import 'layout/layout_service.dart';
|
||||
import 'layout/layout_setting_listener.dart';
|
||||
import 'row/row_cache.dart';
|
||||
import 'group/group_listener.dart';
|
||||
import 'row/row_service.dart';
|
||||
|
||||
typedef OnGroupConfigurationChanged = void Function(List<GroupSettingPB>);
|
||||
typedef OnGroupByField = void Function(List<GroupPB>);
|
||||
@ -174,28 +172,6 @@ class DatabaseController {
|
||||
});
|
||||
}
|
||||
|
||||
Future<Either<RowMetaPB, FlowyError>> createRow({
|
||||
RowId? startRowId,
|
||||
String? groupId,
|
||||
bool fromBeginning = false,
|
||||
void Function(RowDataBuilder builder)? withCells,
|
||||
}) {
|
||||
Map<String, String>? cellDataByFieldId;
|
||||
|
||||
if (withCells != null) {
|
||||
final rowBuilder = RowDataBuilder();
|
||||
withCells(rowBuilder);
|
||||
cellDataByFieldId = rowBuilder.build();
|
||||
}
|
||||
|
||||
return _databaseViewBackendSvc.createRow(
|
||||
startRowId: startRowId,
|
||||
groupId: groupId,
|
||||
cellDataByFieldId: cellDataByFieldId,
|
||||
fromBeginning: fromBeginning,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveGroupRow({
|
||||
required RowMetaPB fromRow,
|
||||
required String fromGroupId,
|
||||
@ -385,27 +361,3 @@ class DatabaseController {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RowDataBuilder {
|
||||
final _cellDataByFieldId = <String, String>{};
|
||||
|
||||
void insertText(FieldInfo fieldInfo, String text) {
|
||||
assert(fieldInfo.fieldType == FieldType.RichText);
|
||||
_cellDataByFieldId[fieldInfo.field.id] = text;
|
||||
}
|
||||
|
||||
void insertNumber(FieldInfo fieldInfo, int num) {
|
||||
assert(fieldInfo.fieldType == FieldType.Number);
|
||||
_cellDataByFieldId[fieldInfo.field.id] = num.toString();
|
||||
}
|
||||
|
||||
void insertDate(FieldInfo fieldInfo, DateTime date) {
|
||||
assert(FieldType.DateTime == fieldInfo.fieldType);
|
||||
final timestamp = date.millisecondsSinceEpoch ~/ 1000;
|
||||
_cellDataByFieldId[fieldInfo.field.id] = timestamp.toString();
|
||||
}
|
||||
|
||||
Map<String, String> build() {
|
||||
return _cellDataByFieldId;
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart';
|
||||
|
||||
class DatabaseViewBackendService {
|
||||
final String viewId;
|
||||
@ -31,29 +30,6 @@ class DatabaseViewBackendService {
|
||||
return DatabaseEventGetDatabase(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<RowMetaPB, FlowyError>> createRow({
|
||||
RowId? startRowId,
|
||||
String? groupId,
|
||||
Map<String, String>? cellDataByFieldId,
|
||||
bool fromBeginning = false,
|
||||
}) {
|
||||
final payload = CreateRowPayloadPB.create()..viewId = viewId;
|
||||
|
||||
if (!fromBeginning || startRowId != null) {
|
||||
payload.startRowId = startRowId ?? "";
|
||||
}
|
||||
|
||||
if (groupId != null) {
|
||||
payload.groupId = groupId;
|
||||
}
|
||||
|
||||
if (cellDataByFieldId != null && cellDataByFieldId.isNotEmpty) {
|
||||
payload.data = RowDataPB(cellDataByFieldId: cellDataByFieldId);
|
||||
}
|
||||
|
||||
return DatabaseEventCreateRow(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveGroupRow({
|
||||
required RowId fromRowId,
|
||||
required String fromGroupId,
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_service.dart';
|
||||
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/protobuf/flowy-database2/protobuf.dart';
|
||||
|
||||
@ -38,26 +37,30 @@ class FieldServices {
|
||||
}
|
||||
|
||||
Future<void> delete() async {
|
||||
await fieldBackendService.deleteField();
|
||||
await fieldBackendService.delete();
|
||||
}
|
||||
|
||||
Future<void> duplicate() async {
|
||||
await fieldBackendService.duplicateField();
|
||||
await fieldBackendService.duplicate();
|
||||
}
|
||||
|
||||
Future<void> insertLeft() async {
|
||||
await TypeOptionBackendService.createFieldTypeOption(
|
||||
await FieldBackendService.createField(
|
||||
viewId: viewId,
|
||||
position: CreateFieldPosition.Before,
|
||||
targetFieldId: fieldId,
|
||||
position: OrderObjectPositionPB(
|
||||
position: OrderObjectPositionTypePB.Before,
|
||||
objectId: fieldId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> insertRight() async {
|
||||
await TypeOptionBackendService.createFieldTypeOption(
|
||||
await FieldBackendService.createField(
|
||||
viewId: viewId,
|
||||
position: CreateFieldPosition.After,
|
||||
targetFieldId: fieldId,
|
||||
position: OrderObjectPositionPB(
|
||||
position: OrderObjectPositionTypePB.After,
|
||||
objectId: fieldId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
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';
|
||||
@ -77,22 +76,14 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
_logIfError(result);
|
||||
},
|
||||
insertLeft: () async {
|
||||
final result = await TypeOptionBackendService.createFieldTypeOption(
|
||||
viewId: viewId,
|
||||
position: CreateFieldPosition.Before,
|
||||
targetFieldId: field.id,
|
||||
);
|
||||
final result = await fieldService.insertBefore();
|
||||
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,
|
||||
);
|
||||
final result = await fieldService.insertAfter();
|
||||
result.fold(
|
||||
(typeOptionPB) => onFieldInserted?.call(typeOptionPB.field_2.id),
|
||||
(err) => Log.error("Failed creating field $err"),
|
||||
@ -111,14 +102,6 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
);
|
||||
_logIfError(result);
|
||||
},
|
||||
deleteField: () async {
|
||||
final result = await fieldService.deleteField();
|
||||
_logIfError(result);
|
||||
},
|
||||
duplicateField: () async {
|
||||
final result = await fieldService.duplicateField();
|
||||
_logIfError(result);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -151,8 +134,6 @@ class FieldEditorEvent with _$FieldEditorEvent {
|
||||
const factory FieldEditorEvent.insertRight() = _InsertRight;
|
||||
const factory FieldEditorEvent.toggleFieldVisibility() =
|
||||
_ToggleFieldVisiblity;
|
||||
const factory FieldEditorEvent.deleteField() = _DeleteField;
|
||||
const factory FieldEditorEvent.duplicateField() = _DuplicateField;
|
||||
}
|
||||
|
||||
@freezed
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
@ -9,21 +10,101 @@ import 'package:dartz/dartz.dart';
|
||||
///
|
||||
/// You could check out the rust-lib/flowy-database/event_map.rs for more information.
|
||||
class FieldBackendService {
|
||||
FieldBackendService({required this.viewId, required this.fieldId});
|
||||
|
||||
final String viewId;
|
||||
final String fieldId;
|
||||
|
||||
FieldBackendService({required this.viewId, required this.fieldId});
|
||||
static Future<Either<TypeOptionPB, FlowyError>> createField({
|
||||
required String viewId,
|
||||
FieldType fieldType = FieldType.RichText,
|
||||
String? fieldName,
|
||||
Uint8List? typeOptionData,
|
||||
OrderObjectPositionPB? position,
|
||||
}) {
|
||||
final payload = CreateFieldPayloadPB(
|
||||
viewId: viewId,
|
||||
fieldType: fieldType,
|
||||
fieldName: fieldName,
|
||||
typeOptionData: typeOptionData,
|
||||
fieldPosition: position,
|
||||
);
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveField(int fromIndex, int toIndex) {
|
||||
final payload = MoveFieldPayloadPB.create()
|
||||
..viewId = viewId
|
||||
..fieldId = fieldId
|
||||
..fromIndex = fromIndex
|
||||
..toIndex = toIndex;
|
||||
return DatabaseEventCreateField(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<TypeOptionPB, FlowyError>> insertBefore({
|
||||
FieldType fieldType = FieldType.RichText,
|
||||
String? fieldName,
|
||||
Uint8List? typeOptionData,
|
||||
}) {
|
||||
return createField(
|
||||
viewId: viewId,
|
||||
fieldType: fieldType,
|
||||
fieldName: fieldName,
|
||||
typeOptionData: typeOptionData,
|
||||
position: OrderObjectPositionPB(
|
||||
position: OrderObjectPositionTypePB.Before,
|
||||
objectId: fieldId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<TypeOptionPB, FlowyError>> insertAfter({
|
||||
FieldType fieldType = FieldType.RichText,
|
||||
String? fieldName,
|
||||
Uint8List? typeOptionData,
|
||||
}) {
|
||||
return createField(
|
||||
viewId: viewId,
|
||||
fieldType: fieldType,
|
||||
fieldName: fieldName,
|
||||
typeOptionData: typeOptionData,
|
||||
position: OrderObjectPositionPB(
|
||||
position: OrderObjectPositionTypePB.Before,
|
||||
objectId: fieldId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Future<Either<Unit, FlowyError>> moveField({
|
||||
required String viewId,
|
||||
required String fromFieldId,
|
||||
required String toFieldId,
|
||||
}) {
|
||||
final payload = MoveFieldPayloadPB(
|
||||
viewId: viewId,
|
||||
fromFieldId: fromFieldId,
|
||||
toFieldId: toFieldId,
|
||||
);
|
||||
|
||||
return DatabaseEventMoveField(payload).send();
|
||||
}
|
||||
|
||||
static Future<Either<Unit, FlowyError>> deleteField({
|
||||
required String viewId,
|
||||
required String fieldId,
|
||||
}) {
|
||||
final payload = DeleteFieldPayloadPB(
|
||||
viewId: viewId,
|
||||
fieldId: fieldId,
|
||||
);
|
||||
|
||||
return DatabaseEventDeleteField(payload).send();
|
||||
}
|
||||
|
||||
static Future<Either<Unit, FlowyError>> duplicateField({
|
||||
required String viewId,
|
||||
required String fieldId,
|
||||
}) {
|
||||
final payload = DuplicateFieldPayloadPB(
|
||||
viewId: viewId,
|
||||
fieldId: fieldId,
|
||||
);
|
||||
|
||||
return DatabaseEventDuplicateField(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> updateField({
|
||||
String? name,
|
||||
bool? frozen,
|
||||
@ -67,20 +148,12 @@ class FieldBackendService {
|
||||
return DatabaseEventUpdateFieldType(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> deleteField() {
|
||||
final payload = DeleteFieldPayloadPB.create()
|
||||
..viewId = viewId
|
||||
..fieldId = fieldId;
|
||||
|
||||
return DatabaseEventDeleteField(payload).send();
|
||||
Future<Either<Unit, FlowyError>> delete() {
|
||||
return deleteField(viewId: viewId, fieldId: fieldId);
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> duplicateField() {
|
||||
final payload = DuplicateFieldPayloadPB.create()
|
||||
..viewId = viewId
|
||||
..fieldId = fieldId;
|
||||
|
||||
return DatabaseEventDuplicateField(payload).send();
|
||||
Future<Either<Unit, FlowyError>> duplicate() {
|
||||
return duplicateField(viewId: viewId, fieldId: fieldId);
|
||||
}
|
||||
|
||||
Future<Either<TypeOptionPB, FlowyError>> getFieldTypeOptionData({
|
||||
|
@ -1,5 +1,3 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
@ -24,34 +22,4 @@ class TypeOptionBackendService {
|
||||
|
||||
return DatabaseEventCreateSelectOption(payload).send();
|
||||
}
|
||||
|
||||
static Future<Either<TypeOptionPB, FlowyError>> createFieldTypeOption({
|
||||
required String viewId,
|
||||
FieldType fieldType = FieldType.RichText,
|
||||
String? fieldName,
|
||||
Uint8List? typeOptionData,
|
||||
CreateFieldPosition position = CreateFieldPosition.End,
|
||||
String? targetFieldId,
|
||||
}) {
|
||||
final payload = CreateFieldPayloadPB.create()
|
||||
..viewId = viewId
|
||||
..fieldType = fieldType;
|
||||
|
||||
if (fieldName != null) {
|
||||
payload.fieldName = fieldName;
|
||||
}
|
||||
|
||||
if (typeOptionData != null) {
|
||||
payload.typeOptionData = typeOptionData;
|
||||
}
|
||||
|
||||
if (position == CreateFieldPosition.Before ||
|
||||
position == CreateFieldPosition.After && targetFieldId != null) {
|
||||
payload.targetFieldId = targetFieldId!;
|
||||
}
|
||||
|
||||
payload.fieldPosition = position;
|
||||
|
||||
return DatabaseEventCreateField(payload).send();
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,13 @@
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_settings_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
class FieldSettingsBackendService {
|
||||
final String viewId;
|
||||
FieldSettingsBackendService({required this.viewId});
|
||||
|
||||
final String viewId;
|
||||
|
||||
Future<Either<FieldSettingsPB, FlowyError>> getFieldSettings(
|
||||
String fieldId,
|
||||
) {
|
||||
|
@ -1,7 +1,9 @@
|
||||
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/row_entities.pb.dart';
|
||||
|
||||
import '../field/field_info.dart';
|
||||
|
||||
typedef RowId = String;
|
||||
|
||||
@ -12,14 +14,53 @@ class RowBackendService {
|
||||
required this.viewId,
|
||||
});
|
||||
|
||||
Future<Either<RowMetaPB, FlowyError>> createRowAfterRow(RowId rowId) {
|
||||
final payload = CreateRowPayloadPB.create()
|
||||
..viewId = viewId
|
||||
..startRowId = rowId;
|
||||
static Future<Either<RowMetaPB, FlowyError>> createRow({
|
||||
required String viewId,
|
||||
String? groupId,
|
||||
void Function(RowDataBuilder builder)? withCells,
|
||||
OrderObjectPositionTypePB? position,
|
||||
String? targetRowId,
|
||||
}) {
|
||||
final payload = CreateRowPayloadPB(
|
||||
viewId: viewId,
|
||||
groupId: groupId,
|
||||
rowPosition: OrderObjectPositionPB(
|
||||
position: position,
|
||||
objectId: targetRowId,
|
||||
),
|
||||
);
|
||||
|
||||
Map<String, String>? cellDataByFieldId;
|
||||
|
||||
if (withCells != null) {
|
||||
final rowBuilder = RowDataBuilder();
|
||||
withCells(rowBuilder);
|
||||
cellDataByFieldId = rowBuilder.build();
|
||||
}
|
||||
|
||||
if (cellDataByFieldId != null) {
|
||||
payload.data = RowDataPB(cellDataByFieldId: cellDataByFieldId);
|
||||
}
|
||||
|
||||
return DatabaseEventCreateRow(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<RowMetaPB, FlowyError>> createRowBefore(RowId rowId) {
|
||||
return createRow(
|
||||
viewId: viewId,
|
||||
position: OrderObjectPositionTypePB.Before,
|
||||
targetRowId: rowId,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<RowMetaPB, FlowyError>> createRowAfter(RowId rowId) {
|
||||
return createRow(
|
||||
viewId: viewId,
|
||||
position: OrderObjectPositionTypePB.After,
|
||||
targetRowId: rowId,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<OptionalRowPB, FlowyError>> getRow(RowId rowId) {
|
||||
final payload = RowIdPB.create()
|
||||
..viewId = viewId
|
||||
@ -73,16 +114,37 @@ class RowBackendService {
|
||||
|
||||
static Future<Either<Unit, FlowyError>> duplicateRow(
|
||||
String viewId,
|
||||
RowId rowId, [
|
||||
String? groupId,
|
||||
]) {
|
||||
final payload = RowIdPB.create()
|
||||
..viewId = viewId
|
||||
..rowId = rowId;
|
||||
if (groupId != null) {
|
||||
payload.groupId = groupId;
|
||||
}
|
||||
RowId rowId,
|
||||
) {
|
||||
final payload = RowIdPB(
|
||||
viewId: viewId,
|
||||
rowId: rowId,
|
||||
);
|
||||
|
||||
return DatabaseEventDuplicateRow(payload).send();
|
||||
}
|
||||
}
|
||||
|
||||
class RowDataBuilder {
|
||||
final _cellDataByFieldId = <String, String>{};
|
||||
|
||||
void insertText(FieldInfo fieldInfo, String text) {
|
||||
assert(fieldInfo.fieldType == FieldType.RichText);
|
||||
_cellDataByFieldId[fieldInfo.field.id] = text;
|
||||
}
|
||||
|
||||
void insertNumber(FieldInfo fieldInfo, int num) {
|
||||
assert(fieldInfo.fieldType == FieldType.Number);
|
||||
_cellDataByFieldId[fieldInfo.field.id] = num.toString();
|
||||
}
|
||||
|
||||
void insertDate(FieldInfo fieldInfo, DateTime date) {
|
||||
assert(fieldInfo.fieldType == FieldType.DateTime);
|
||||
final timestamp = date.millisecondsSinceEpoch ~/ 1000;
|
||||
_cellDataByFieldId[fieldInfo.field.id] = timestamp.toString();
|
||||
}
|
||||
|
||||
Map<String, String> build() {
|
||||
return _cellDataByFieldId;
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field_settings/field_settings_service.dart';
|
||||
@ -5,7 +7,6 @@ import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_settings_entities.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
|
||||
import '../field/field_service.dart';
|
||||
|
||||
@ -44,25 +45,21 @@ class DatabasePropertyBloc
|
||||
didReceiveFieldUpdate: (fields) {
|
||||
emit(state.copyWith(fieldContexts: fields));
|
||||
},
|
||||
moveField: (fieldId, fromIndex, toIndex) async {
|
||||
moveField: (fromIndex, toIndex) async {
|
||||
if (fromIndex < toIndex) {
|
||||
toIndex--;
|
||||
}
|
||||
final fromId = state.fieldContexts[fromIndex].field.id;
|
||||
final toId = state.fieldContexts[toIndex].field.id;
|
||||
|
||||
final fieldContexts = List<FieldInfo>.from(state.fieldContexts);
|
||||
fieldContexts.insert(
|
||||
toIndex,
|
||||
fieldContexts.removeAt(fromIndex),
|
||||
);
|
||||
fieldContexts.insert(toIndex, fieldContexts.removeAt(fromIndex));
|
||||
emit(state.copyWith(fieldContexts: fieldContexts));
|
||||
|
||||
final fieldBackendService = FieldBackendService(
|
||||
final result = await FieldBackendService.moveField(
|
||||
viewId: viewId,
|
||||
fieldId: fieldId,
|
||||
);
|
||||
|
||||
final result = await fieldBackendService.moveField(
|
||||
fromIndex,
|
||||
toIndex,
|
||||
fromFieldId: fromId,
|
||||
toFieldId: toId,
|
||||
);
|
||||
|
||||
result.fold((l) => null, (r) => Log.error(r));
|
||||
@ -101,11 +98,8 @@ class DatabasePropertyEvent with _$DatabasePropertyEvent {
|
||||
const factory DatabasePropertyEvent.didReceiveFieldUpdate(
|
||||
List<FieldInfo> fields,
|
||||
) = _DidReceiveFieldUpdate;
|
||||
const factory DatabasePropertyEvent.moveField({
|
||||
required String fieldId,
|
||||
required int fromIndex,
|
||||
required int toIndex,
|
||||
}) = _MoveField;
|
||||
const factory DatabasePropertyEvent.moveField(int fromIndex, int toIndex) =
|
||||
_MoveField;
|
||||
}
|
||||
|
||||
@freezed
|
||||
|
@ -80,11 +80,16 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
_startListening();
|
||||
await _openGrid(emit);
|
||||
},
|
||||
createBottomRow: (groupId) async {
|
||||
final startRowId = groupControllers[groupId]?.lastRow()?.id;
|
||||
final result = await databaseController.createRow(
|
||||
createHeaderRow: (groupId) async {
|
||||
final rowId = groupControllers[groupId]?.firstRow()?.id;
|
||||
final position = rowId == null
|
||||
? OrderObjectPositionTypePB.Start
|
||||
: OrderObjectPositionTypePB.Before;
|
||||
final result = await RowBackendService.createRow(
|
||||
viewId: databaseController.viewId,
|
||||
groupId: groupId,
|
||||
startRowId: startRowId,
|
||||
position: position,
|
||||
targetRowId: rowId,
|
||||
);
|
||||
|
||||
result.fold(
|
||||
@ -94,10 +99,16 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
},
|
||||
createHeaderRow: (String groupId) async {
|
||||
final result = await databaseController.createRow(
|
||||
createBottomRow: (groupId) async {
|
||||
final rowId = groupControllers[groupId]?.lastRow()?.id;
|
||||
final position = rowId == null
|
||||
? OrderObjectPositionTypePB.End
|
||||
: OrderObjectPositionTypePB.After;
|
||||
final result = await RowBackendService.createRow(
|
||||
viewId: databaseController.viewId,
|
||||
groupId: groupId,
|
||||
fromBeginning: true,
|
||||
position: position,
|
||||
targetRowId: rowId,
|
||||
);
|
||||
|
||||
result.fold(
|
||||
|
@ -40,6 +40,11 @@ class GroupController {
|
||||
}
|
||||
}
|
||||
|
||||
RowMetaPB? firstRow() {
|
||||
if (group.rows.isEmpty) return null;
|
||||
return group.rows.first;
|
||||
}
|
||||
|
||||
RowMetaPB? lastRow() {
|
||||
if (group.rows.isEmpty) return null;
|
||||
return group.rows.last;
|
||||
|
@ -269,7 +269,6 @@ class _DesktopBoardContentState extends State<DesktopBoardContent> {
|
||||
rowCache: rowCache,
|
||||
cardData: groupData.group.groupId,
|
||||
groupingFieldId: groupItem.fieldInfo.id,
|
||||
groupId: groupData.group.groupId,
|
||||
isEditing: isEditing,
|
||||
cellBuilder: cellBuilder,
|
||||
renderHook: renderHook,
|
||||
|
@ -146,19 +146,18 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
||||
(settings) async {
|
||||
final dateField = _getCalendarFieldInfo(settings.fieldId);
|
||||
if (dateField != null) {
|
||||
final newRow = await databaseController
|
||||
.createRow(
|
||||
withCells: (builder) => builder.insertDate(dateField, date),
|
||||
)
|
||||
.then(
|
||||
(result) => result.fold(
|
||||
(newRow) => newRow,
|
||||
(err) {
|
||||
Log.error(err);
|
||||
return null;
|
||||
},
|
||||
),
|
||||
);
|
||||
final newRow = await RowBackendService.createRow(
|
||||
viewId: viewId,
|
||||
withCells: (builder) => builder.insertDate(dateField, date),
|
||||
).then(
|
||||
(result) => result.fold(
|
||||
(newRow) => newRow,
|
||||
(err) {
|
||||
Log.error(err);
|
||||
return null;
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
if (newRow != null) {
|
||||
final event = await _loadEvent(newRow.id);
|
||||
|
@ -28,7 +28,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
await _openGrid(emit);
|
||||
},
|
||||
createRow: () async {
|
||||
final result = await databaseController.createRow();
|
||||
final result = await RowBackendService.createRow(viewId: viewId);
|
||||
result.fold(
|
||||
(createdRow) => emit(state.copyWith(createdRow: createdRow)),
|
||||
(err) => Log.error(err),
|
||||
@ -88,6 +88,8 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
);
|
||||
}
|
||||
|
||||
String get viewId => databaseController.viewId;
|
||||
|
||||
RowCache getRowCache(RowId rowId) {
|
||||
return databaseController.rowCache;
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_settings_entities.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
@ -49,8 +48,8 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
|
||||
endEditingField: () {
|
||||
emit(state.copyWith(editingFieldId: null, newFieldId: null));
|
||||
},
|
||||
moveField: (field, fromIndex, toIndex) async {
|
||||
await _moveField(field, fromIndex, toIndex, emit);
|
||||
moveField: (fromIndex, toIndex) async {
|
||||
await _moveField(fromIndex, toIndex, emit);
|
||||
},
|
||||
);
|
||||
},
|
||||
@ -58,17 +57,22 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
|
||||
}
|
||||
|
||||
Future<void> _moveField(
|
||||
FieldPB field,
|
||||
int fromIndex,
|
||||
int toIndex,
|
||||
Emitter<GridHeaderState> emit,
|
||||
) async {
|
||||
final fromId = state.fields[fromIndex].id;
|
||||
final toId = state.fields[toIndex].id;
|
||||
|
||||
final fields = List<FieldInfo>.from(state.fields);
|
||||
fields.insert(toIndex, fields.removeAt(fromIndex));
|
||||
emit(state.copyWith(fields: fields));
|
||||
|
||||
final fieldService = FieldBackendService(viewId: viewId, fieldId: field.id);
|
||||
final result = await fieldService.moveField(fromIndex, toIndex);
|
||||
final result = await FieldBackendService.moveField(
|
||||
viewId: viewId,
|
||||
fromFieldId: fromId,
|
||||
toFieldId: toId,
|
||||
);
|
||||
result.fold((l) {}, (err) => Log.error(err));
|
||||
}
|
||||
|
||||
@ -92,7 +96,6 @@ class GridHeaderEvent with _$GridHeaderEvent {
|
||||
_StartEditingNewField;
|
||||
const factory GridHeaderEvent.endEditingField() = _EndEditingField;
|
||||
const factory GridHeaderEvent.moveField(
|
||||
FieldPB field,
|
||||
int fromIndex,
|
||||
int toIndex,
|
||||
) = _MoveField;
|
||||
|
@ -36,7 +36,7 @@ class RowBloc extends Bloc<RowEvent, RowState> {
|
||||
await _startListening();
|
||||
},
|
||||
createRow: () {
|
||||
_rowBackendSvc.createRowAfterRow(rowId);
|
||||
_rowBackendSvc.createRowAfter(rowId);
|
||||
},
|
||||
didReceiveCells: (cellByFieldId, reason) async {
|
||||
cellByFieldId
|
||||
|
@ -30,12 +30,12 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
|
||||
),
|
||||
);
|
||||
},
|
||||
deleteField: (fieldId) {
|
||||
final fieldService = FieldBackendService(
|
||||
deleteField: (fieldId) async {
|
||||
final result = await FieldBackendService.deleteField(
|
||||
viewId: rowController.viewId,
|
||||
fieldId: fieldId,
|
||||
);
|
||||
fieldService.deleteField();
|
||||
result.fold((l) {}, (err) => Log.error(err));
|
||||
},
|
||||
toggleFieldVisibility: (fieldId) async {
|
||||
final fieldInfo = state.allCells
|
||||
@ -57,15 +57,8 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
},
|
||||
reorderField:
|
||||
(reorderedFieldId, targetFieldId, fromIndex, toIndex) async {
|
||||
await _reorderField(
|
||||
reorderedFieldId,
|
||||
targetFieldId,
|
||||
fromIndex,
|
||||
toIndex,
|
||||
emit,
|
||||
);
|
||||
reorderField: (fromIndex, toIndex) async {
|
||||
await _reorderField(fromIndex, toIndex, emit);
|
||||
},
|
||||
toggleHiddenFieldVisibility: () {
|
||||
final showHiddenFields = !state.showHiddenFields;
|
||||
@ -127,28 +120,24 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
|
||||
}
|
||||
|
||||
Future<void> _reorderField(
|
||||
String reorderedFieldId,
|
||||
String targetFieldId,
|
||||
int fromIndex,
|
||||
int toIndex,
|
||||
Emitter<RowDetailState> emit,
|
||||
) async {
|
||||
if (fromIndex < toIndex) {
|
||||
toIndex--;
|
||||
}
|
||||
final fromId = state.visibleCells[fromIndex].fieldId;
|
||||
final toId = state.visibleCells[toIndex].fieldId;
|
||||
|
||||
final cells = List<DatabaseCellContext>.from(state.visibleCells);
|
||||
cells.insert(toIndex, cells.removeAt(fromIndex));
|
||||
emit(state.copyWith(visibleCells: cells));
|
||||
|
||||
final fromIndexInAllFields =
|
||||
state.allCells.indexWhere((cell) => cell.fieldId == reorderedFieldId);
|
||||
final toIndexInAllFields =
|
||||
state.allCells.indexWhere((cell) => cell.fieldId == targetFieldId);
|
||||
|
||||
final fieldService = FieldBackendService(
|
||||
final result = await FieldBackendService.moveField(
|
||||
viewId: rowController.viewId,
|
||||
fieldId: reorderedFieldId,
|
||||
);
|
||||
final result = await fieldService.moveField(
|
||||
fromIndexInAllFields,
|
||||
toIndexInAllFields,
|
||||
fromFieldId: fromId,
|
||||
toFieldId: toId,
|
||||
);
|
||||
result.fold((l) {}, (err) => Log.error(err));
|
||||
}
|
||||
@ -161,8 +150,6 @@ class RowDetailEvent with _$RowDetailEvent {
|
||||
const factory RowDetailEvent.toggleFieldVisibility(String fieldId) =
|
||||
_ToggleFieldVisibility;
|
||||
const factory RowDetailEvent.reorderField(
|
||||
String reorderFieldID,
|
||||
String targetFieldID,
|
||||
int fromIndex,
|
||||
int toIndex,
|
||||
) = _ReorderField;
|
||||
|
@ -263,18 +263,19 @@ enum FieldAction {
|
||||
break;
|
||||
case FieldAction.duplicate:
|
||||
PopoverContainer.of(context).close();
|
||||
context
|
||||
.read<FieldEditorBloc>()
|
||||
.add(const FieldEditorEvent.duplicateField());
|
||||
FieldBackendService.duplicateField(
|
||||
viewId: viewId,
|
||||
fieldId: fieldInfo.id,
|
||||
);
|
||||
break;
|
||||
case FieldAction.delete:
|
||||
NavigatorAlertDialog(
|
||||
title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
|
||||
confirm: () {
|
||||
FieldBackendService(
|
||||
FieldBackendService.deleteField(
|
||||
viewId: viewId,
|
||||
fieldId: fieldInfo.id,
|
||||
).deleteField();
|
||||
);
|
||||
},
|
||||
).show(context);
|
||||
PopoverContainer.of(context).close();
|
||||
|
@ -3,6 +3,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/field/bottom_sheet_create_field.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_service.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/application/grid_bloc.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/application/grid_header_bloc.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/mobile_field_cell.dart';
|
||||
@ -15,7 +16,6 @@ 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_cell.dart';
|
||||
|
||||
@ -134,12 +134,14 @@ class _GridHeaderState extends State<_GridHeader> {
|
||||
needsLongPressDraggable: PlatformExtension.isMobile,
|
||||
footer: _CellTrailing(viewId: widget.viewId),
|
||||
onReorder: (int oldIndex, int newIndex) {
|
||||
_onReorder(
|
||||
cells,
|
||||
oldIndex,
|
||||
context,
|
||||
newIndex,
|
||||
);
|
||||
// to offset removing the first field from `state.fields`
|
||||
if (PlatformExtension.isMobile) {
|
||||
oldIndex++;
|
||||
newIndex++;
|
||||
}
|
||||
context
|
||||
.read<GridHeaderBloc>()
|
||||
.add(GridHeaderEvent.moveField(oldIndex, newIndex));
|
||||
},
|
||||
children: cells,
|
||||
),
|
||||
@ -148,22 +150,6 @@ class _GridHeaderState extends State<_GridHeader> {
|
||||
);
|
||||
}
|
||||
|
||||
void _onReorder(
|
||||
List<Widget> cells,
|
||||
int oldIndex,
|
||||
BuildContext context,
|
||||
int newIndex,
|
||||
) {
|
||||
if (cells.length > oldIndex) {
|
||||
final field = PlatformExtension.isDesktop
|
||||
? (cells[oldIndex] as GridFieldCell).fieldInfo.field
|
||||
: (cells[oldIndex] as MobileFieldButton).fieldInfo.field;
|
||||
context
|
||||
.read<GridHeaderBloc>()
|
||||
.add(GridHeaderEvent.moveField(field, oldIndex, newIndex));
|
||||
}
|
||||
}
|
||||
|
||||
Widget _cellLeading(FieldInfo? fieldInfo) {
|
||||
if (PlatformExtension.isDesktop) {
|
||||
return SizedBox(width: GridSize.leadingHeaderPadding);
|
||||
@ -245,7 +231,7 @@ class _CreateFieldButtonState extends State<CreateFieldButton> {
|
||||
if (PlatformExtension.isMobile) {
|
||||
showCreateFieldBottomSheet(context, widget.viewId);
|
||||
} else {
|
||||
final result = await TypeOptionBackendService.createFieldTypeOption(
|
||||
final result = await FieldBackendService.createField(
|
||||
viewId: widget.viewId,
|
||||
);
|
||||
result.fold(
|
||||
|
@ -1,113 +1,107 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/row/row_service.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../layout/sizes.dart';
|
||||
|
||||
class RowActions extends StatelessWidget {
|
||||
final String viewId;
|
||||
final RowId rowId;
|
||||
final String? groupId;
|
||||
const RowActions({
|
||||
class RowActionMenu extends StatelessWidget {
|
||||
const RowActionMenu({
|
||||
super.key,
|
||||
required this.viewId,
|
||||
required this.rowId,
|
||||
this.actions = RowAction.values,
|
||||
this.groupId,
|
||||
super.key,
|
||||
});
|
||||
|
||||
const RowActionMenu.board({
|
||||
super.key,
|
||||
required this.viewId,
|
||||
required this.rowId,
|
||||
required this.groupId,
|
||||
}) : actions = const [RowAction.duplicate, RowAction.delete];
|
||||
|
||||
final String viewId;
|
||||
final RowId rowId;
|
||||
final List<RowAction> actions;
|
||||
final String? groupId;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final cells = _RowAction.values
|
||||
.where((value) => value.enable())
|
||||
.map((action) => _actionCell(context, action))
|
||||
.toList();
|
||||
final cells =
|
||||
actions.map((action) => _actionCell(context, action)).toList();
|
||||
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
controller: ScrollController(),
|
||||
itemCount: cells.length,
|
||||
separatorBuilder: (context, index) {
|
||||
return VSpace(GridSize.typeOptionSeparatorHeight);
|
||||
},
|
||||
physics: StyledScrollPhysics(),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return cells[index];
|
||||
},
|
||||
return SeparatedColumn(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
separatorBuilder: () => VSpace(GridSize.typeOptionSeparatorHeight),
|
||||
children: cells,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _actionCell(BuildContext context, _RowAction action) {
|
||||
Widget _actionCell(BuildContext context, RowAction action) {
|
||||
Widget icon = FlowySvg(action.icon);
|
||||
if (action == RowAction.insertAbove) {
|
||||
icon = RotatedBox(quarterTurns: 1, child: icon);
|
||||
}
|
||||
return SizedBox(
|
||||
height: GridSize.popoverItemHeight,
|
||||
child: FlowyButton(
|
||||
hoverColor: AFThemeExtension.of(context).lightGreyHover,
|
||||
useIntrinsicWidth: true,
|
||||
text: FlowyText.medium(
|
||||
action.title(),
|
||||
color: action.enable()
|
||||
? AFThemeExtension.of(context).textColor
|
||||
: Theme.of(context).disabledColor,
|
||||
),
|
||||
text: FlowyText.medium(action.text, overflow: TextOverflow.ellipsis),
|
||||
onTap: () {
|
||||
if (action.enable()) {
|
||||
action.performAction(context, viewId, rowId);
|
||||
}
|
||||
action.performAction(context, viewId, rowId);
|
||||
PopoverContainer.of(context).close();
|
||||
},
|
||||
leftIcon: FlowySvg(
|
||||
action.icon(),
|
||||
color: Theme.of(context).iconTheme.color,
|
||||
),
|
||||
leftIcon: icon,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum _RowAction {
|
||||
delete,
|
||||
enum RowAction {
|
||||
insertAbove,
|
||||
insertBelow,
|
||||
duplicate,
|
||||
}
|
||||
delete;
|
||||
|
||||
extension _RowActionExtension on _RowAction {
|
||||
FlowySvgData icon() {
|
||||
switch (this) {
|
||||
case _RowAction.duplicate:
|
||||
return FlowySvgs.copy_s;
|
||||
case _RowAction.delete:
|
||||
return FlowySvgs.delete_s;
|
||||
}
|
||||
FlowySvgData get icon {
|
||||
return switch (this) {
|
||||
insertAbove => FlowySvgs.arrow_s,
|
||||
insertBelow => FlowySvgs.add_s,
|
||||
duplicate => FlowySvgs.copy_s,
|
||||
delete => FlowySvgs.delete_s,
|
||||
};
|
||||
}
|
||||
|
||||
String title() {
|
||||
switch (this) {
|
||||
case _RowAction.duplicate:
|
||||
return LocaleKeys.grid_row_duplicate.tr();
|
||||
case _RowAction.delete:
|
||||
return LocaleKeys.grid_row_delete.tr();
|
||||
}
|
||||
}
|
||||
|
||||
bool enable() {
|
||||
switch (this) {
|
||||
case _RowAction.duplicate:
|
||||
case _RowAction.delete:
|
||||
return true;
|
||||
}
|
||||
String get text {
|
||||
return switch (this) {
|
||||
insertAbove => LocaleKeys.grid_row_insertRecordAbove.tr(),
|
||||
insertBelow => LocaleKeys.grid_row_insertRecordBelow.tr(),
|
||||
duplicate => LocaleKeys.grid_row_duplicate.tr(),
|
||||
delete => LocaleKeys.grid_row_delete.tr(),
|
||||
};
|
||||
}
|
||||
|
||||
void performAction(BuildContext context, String viewId, String rowId) {
|
||||
switch (this) {
|
||||
case _RowAction.duplicate:
|
||||
case insertAbove:
|
||||
case insertBelow:
|
||||
final position = this == insertAbove
|
||||
? OrderObjectPositionTypePB.Before
|
||||
: OrderObjectPositionTypePB.After;
|
||||
RowBackendService.createRow(
|
||||
viewId: viewId,
|
||||
position: position,
|
||||
targetRowId: rowId,
|
||||
);
|
||||
break;
|
||||
case duplicate:
|
||||
RowBackendService.duplicateRow(viewId, rowId);
|
||||
break;
|
||||
case _RowAction.delete:
|
||||
case delete:
|
||||
RowBackendService.deleteRow(viewId, rowId);
|
||||
break;
|
||||
}
|
||||
|
@ -113,25 +113,19 @@ class _RowLeading extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _RowLeadingState extends State<_RowLeading> {
|
||||
late final PopoverController popoverController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
popoverController = PopoverController();
|
||||
}
|
||||
final PopoverController popoverController = PopoverController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppFlowyPopover(
|
||||
controller: popoverController,
|
||||
triggerActions: PopoverTriggerFlags.none,
|
||||
constraints: BoxConstraints.loose(const Size(140, 200)),
|
||||
constraints: BoxConstraints.loose(const Size(176, 200)),
|
||||
direction: PopoverDirection.rightWithCenterAligned,
|
||||
margin: const EdgeInsets.all(6),
|
||||
popupBuilder: (BuildContext popoverContext) {
|
||||
margin: const EdgeInsets.symmetric(horizontal: 6, vertical: 8),
|
||||
popupBuilder: (_) {
|
||||
final bloc = context.read<RowBloc>();
|
||||
return RowActions(
|
||||
return RowActionMenu(
|
||||
viewId: bloc.viewId,
|
||||
rowId: bloc.rowId,
|
||||
);
|
||||
|
@ -138,11 +138,13 @@ class _RowCardState<T> extends State<RowCard<T>> {
|
||||
triggerActions: PopoverTriggerFlags.none,
|
||||
constraints: BoxConstraints.loose(const Size(140, 200)),
|
||||
direction: PopoverDirection.rightWithCenterAligned,
|
||||
popupBuilder: (_) => RowActions(
|
||||
viewId: _cardBloc.viewId,
|
||||
rowId: _cardBloc.rowMeta.id,
|
||||
groupId: widget.groupId,
|
||||
),
|
||||
popupBuilder: (_) {
|
||||
return RowActionMenu.board(
|
||||
viewId: _cardBloc.viewId,
|
||||
rowId: _cardBloc.rowMeta.id,
|
||||
groupId: widget.groupId,
|
||||
);
|
||||
},
|
||||
child: RowCardContainer(
|
||||
buildAccessoryWhen: () => state.isEditing == false,
|
||||
accessories: [
|
||||
|
@ -25,7 +25,6 @@ class RowActionList extends StatelessWidget {
|
||||
RowDetailPageDuplicateButton(
|
||||
viewId: rowController.viewId,
|
||||
rowId: rowController.rowId,
|
||||
groupId: rowController.groupId,
|
||||
),
|
||||
const VSpace(4.0),
|
||||
RowDetailPageDeleteButton(
|
||||
@ -65,16 +64,15 @@ class RowDetailPageDeleteButton extends StatelessWidget {
|
||||
}
|
||||
|
||||
class RowDetailPageDuplicateButton extends StatelessWidget {
|
||||
final String viewId;
|
||||
final String rowId;
|
||||
final String? groupId;
|
||||
const RowDetailPageDuplicateButton({
|
||||
super.key,
|
||||
required this.viewId,
|
||||
required this.rowId,
|
||||
this.groupId,
|
||||
});
|
||||
|
||||
final String viewId;
|
||||
final String rowId;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
@ -83,7 +81,7 @@ class RowDetailPageDuplicateButton extends StatelessWidget {
|
||||
text: FlowyText.regular(LocaleKeys.grid_row_duplicate.tr()),
|
||||
leftIcon: const FlowySvg(FlowySvgs.copy_s),
|
||||
onTap: () {
|
||||
RowBackendService.duplicateRow(viewId, rowId, groupId);
|
||||
RowBackendService.duplicateRow(viewId, rowId);
|
||||
FlowyOverlay.pop(context);
|
||||
},
|
||||
),
|
||||
|
@ -4,7 +4,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_service.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_service.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/application/row/row_detail_bloc.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_cell.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_editor.dart';
|
||||
@ -57,22 +57,10 @@ class RowPropertyList extends StatelessWidget {
|
||||
return ReorderableListView(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
onReorder: (oldIndex, newIndex) {
|
||||
// when reorderiing downwards, need to update index
|
||||
if (oldIndex < newIndex) {
|
||||
newIndex--;
|
||||
}
|
||||
final reorderedFieldId = children[oldIndex].cellContext.fieldId;
|
||||
final targetFieldId = children[newIndex].cellContext.fieldId;
|
||||
|
||||
context.read<RowDetailBloc>().add(
|
||||
RowDetailEvent.reorderField(
|
||||
reorderedFieldId,
|
||||
targetFieldId,
|
||||
oldIndex,
|
||||
newIndex,
|
||||
),
|
||||
);
|
||||
onReorder: (from, to) {
|
||||
context
|
||||
.read<RowDetailBloc>()
|
||||
.add(RowDetailEvent.reorderField(from, to));
|
||||
},
|
||||
buildDefaultDragHandles: false,
|
||||
proxyDecorator: (child, index, animation) => Material(
|
||||
@ -391,7 +379,7 @@ class _CreateRowFieldButtonState extends State<CreateRowFieldButton> {
|
||||
),
|
||||
hoverColor: AFThemeExtension.of(context).lightGreyHover,
|
||||
onTap: () async {
|
||||
final result = await TypeOptionBackendService.createFieldTypeOption(
|
||||
final result = await FieldBackendService.createField(
|
||||
viewId: widget.viewId,
|
||||
);
|
||||
result.fold(
|
||||
|
@ -95,13 +95,9 @@ class _DatabasePropertyListState extends State<DatabasePropertyList> {
|
||||
buildDefaultDragHandles: false,
|
||||
shrinkWrap: true,
|
||||
onReorder: (from, to) {
|
||||
context.read<DatabasePropertyBloc>().add(
|
||||
DatabasePropertyEvent.moveField(
|
||||
fieldId: cells[from].fieldInfo.id,
|
||||
fromIndex: from,
|
||||
toIndex: to,
|
||||
),
|
||||
);
|
||||
context
|
||||
.read<DatabasePropertyBloc>()
|
||||
.add(DatabasePropertyEvent.moveField(from, to));
|
||||
},
|
||||
onReorderStart: (_) => _popoverMutex.close(),
|
||||
padding: const EdgeInsets.symmetric(vertical: 6.0),
|
||||
|
@ -3,18 +3,16 @@ import 'package:appflowy/plugins/database_view/application/cell/cell_controller_
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_editor_bloc.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_info.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_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/row/row_service.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/application/row/row_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pbserver.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../../util.dart';
|
||||
|
||||
@ -34,10 +32,6 @@ class GridTestContext {
|
||||
return gridController.fieldController;
|
||||
}
|
||||
|
||||
Future<Either<RowMetaPB, FlowyError>> createRow() async {
|
||||
return gridController.createRow();
|
||||
}
|
||||
|
||||
Future<CellController> makeCellController(
|
||||
String fieldId,
|
||||
int rowIndex,
|
||||
@ -135,7 +129,7 @@ class GridTestContext {
|
||||
Future<FieldEditorBloc> createFieldEditor({
|
||||
required DatabaseController databaseController,
|
||||
}) async {
|
||||
final result = await TypeOptionBackendService.createFieldTypeOption(
|
||||
final result = await FieldBackendService.createField(
|
||||
viewId: databaseController.viewId,
|
||||
);
|
||||
await gridResponseFuture();
|
||||
@ -211,7 +205,7 @@ class AppFlowyGridCellTest {
|
||||
}
|
||||
|
||||
Future<void> createTestRow() async {
|
||||
await context.createRow();
|
||||
await RowBackendService.createRow(viewId: context.gridView.id);
|
||||
}
|
||||
|
||||
Future<SelectOptionCellController> makeSelectOptionCellController(
|
||||
|
18
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
18
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -883,7 +883,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=32218f1b6a9b09a9bbaa1835749e016246c092d4#32218f1b6a9b09a9bbaa1835749e016246c092d4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ac2ab049e367009b59c2e411c28580609a19e8b8#ac2ab049e367009b59c2e411c28580609a19e8b8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -902,7 +902,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-database"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=32218f1b6a9b09a9bbaa1835749e016246c092d4#32218f1b6a9b09a9bbaa1835749e016246c092d4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ac2ab049e367009b59c2e411c28580609a19e8b8#ac2ab049e367009b59c2e411c28580609a19e8b8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -932,7 +932,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-derive"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=32218f1b6a9b09a9bbaa1835749e016246c092d4#32218f1b6a9b09a9bbaa1835749e016246c092d4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ac2ab049e367009b59c2e411c28580609a19e8b8#ac2ab049e367009b59c2e411c28580609a19e8b8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -944,7 +944,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-document"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=32218f1b6a9b09a9bbaa1835749e016246c092d4#32218f1b6a9b09a9bbaa1835749e016246c092d4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ac2ab049e367009b59c2e411c28580609a19e8b8#ac2ab049e367009b59c2e411c28580609a19e8b8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@ -963,7 +963,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=32218f1b6a9b09a9bbaa1835749e016246c092d4#32218f1b6a9b09a9bbaa1835749e016246c092d4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ac2ab049e367009b59c2e411c28580609a19e8b8#ac2ab049e367009b59c2e411c28580609a19e8b8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -977,7 +977,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-folder"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=32218f1b6a9b09a9bbaa1835749e016246c092d4#32218f1b6a9b09a9bbaa1835749e016246c092d4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ac2ab049e367009b59c2e411c28580609a19e8b8#ac2ab049e367009b59c2e411c28580609a19e8b8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@ -1019,7 +1019,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-persistence"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=32218f1b6a9b09a9bbaa1835749e016246c092d4#32218f1b6a9b09a9bbaa1835749e016246c092d4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ac2ab049e367009b59c2e411c28580609a19e8b8#ac2ab049e367009b59c2e411c28580609a19e8b8"
|
||||
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=32218f1b6a9b09a9bbaa1835749e016246c092d4#32218f1b6a9b09a9bbaa1835749e016246c092d4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ac2ab049e367009b59c2e411c28580609a19e8b8#ac2ab049e367009b59c2e411c28580609a19e8b8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1066,7 +1066,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-user"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=32218f1b6a9b09a9bbaa1835749e016246c092d4#32218f1b6a9b09a9bbaa1835749e016246c092d4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ac2ab049e367009b59c2e411c28580609a19e8b8#ac2ab049e367009b59c2e411c28580609a19e8b8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
|
@ -67,14 +67,14 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "9da
|
||||
# To switch to the local path, run:
|
||||
# scripts/tool/update_collab_source.sh
|
||||
# ⚠️⚠️⚠️️
|
||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "32218f1b6a9b09a9bbaa1835749e016246c092d4" }
|
||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "32218f1b6a9b09a9bbaa1835749e016246c092d4" }
|
||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "32218f1b6a9b09a9bbaa1835749e016246c092d4" }
|
||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "32218f1b6a9b09a9bbaa1835749e016246c092d4" }
|
||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "32218f1b6a9b09a9bbaa1835749e016246c092d4" }
|
||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "32218f1b6a9b09a9bbaa1835749e016246c092d4" }
|
||||
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "32218f1b6a9b09a9bbaa1835749e016246c092d4" }
|
||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "32218f1b6a9b09a9bbaa1835749e016246c092d4" }
|
||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ac2ab049e367009b59c2e411c28580609a19e8b8" }
|
||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ac2ab049e367009b59c2e411c28580609a19e8b8" }
|
||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ac2ab049e367009b59c2e411c28580609a19e8b8" }
|
||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ac2ab049e367009b59c2e411c28580609a19e8b8" }
|
||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ac2ab049e367009b59c2e411c28580609a19e8b8" }
|
||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ac2ab049e367009b59c2e411c28580609a19e8b8" }
|
||||
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ac2ab049e367009b59c2e411c28580609a19e8b8" }
|
||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ac2ab049e367009b59c2e411c28580609a19e8b8" }
|
||||
|
||||
|
||||
|
||||
|
@ -157,10 +157,14 @@ export const EditRow = ({
|
||||
|
||||
const onDragEnd: OnDragEndResponder = (result) => {
|
||||
if (!result.destination?.index) return;
|
||||
const fields = cells
|
||||
.filter((cell) => {
|
||||
return fieldsStore[cell.cellIdentifier.fieldId]?.visible;
|
||||
});
|
||||
|
||||
void controller.moveField({
|
||||
fieldId: result.draggableId,
|
||||
fromIndex: result.source.index,
|
||||
toIndex: result.destination.index,
|
||||
fromFieldId: result.draggableId,
|
||||
toFieldId: fields[result.source.index].fieldId,
|
||||
});
|
||||
};
|
||||
|
||||
@ -186,9 +190,8 @@ export const EditRow = ({
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`fixed inset-0 z-10 flex items-center justify-center bg-black/30 backdrop-blur-sm transition-opacity duration-300 ${
|
||||
unveil ? 'opacity-100' : 'opacity-0'
|
||||
}`}
|
||||
className={`fixed inset-0 z-10 flex items-center justify-center bg-black/30 backdrop-blur-sm transition-opacity duration-300 ${unveil ? 'opacity-100' : 'opacity-0'
|
||||
}`}
|
||||
onClick={() => onCloseClick()}
|
||||
>
|
||||
<div
|
||||
@ -220,9 +223,8 @@ export const EditRow = ({
|
||||
<div
|
||||
{...provided.droppableProps}
|
||||
ref={provided.innerRef}
|
||||
className={`flex flex-1 flex-col gap-8 px-8 pb-8 ${
|
||||
showFieldEditor || showChangeOptionsPopup || showDatePicker ? 'overflow-hidden' : 'overflow-auto'
|
||||
}`}
|
||||
className={`flex flex-1 flex-col gap-8 px-8 pb-8 ${showFieldEditor || showChangeOptionsPopup || showDatePicker ? 'overflow-hidden' : 'overflow-auto'
|
||||
}`}
|
||||
>
|
||||
{cells
|
||||
.filter((cell) => {
|
||||
|
@ -54,24 +54,6 @@ export const useDatabase = () => useSnapshot(useContext(DatabaseContext));
|
||||
|
||||
export const useContextDatabase = () => useContext(DatabaseContext);
|
||||
|
||||
export const useGetPrevRowId = () => {
|
||||
const database = useContextDatabase();
|
||||
|
||||
return useCallback(
|
||||
(id: string) => {
|
||||
const rowMetas = database.rowMetas;
|
||||
const index = rowMetas.findIndex((rowMeta) => rowMeta.id === id);
|
||||
|
||||
if (index === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return rowMetas[index - 1].id;
|
||||
},
|
||||
[database]
|
||||
);
|
||||
};
|
||||
|
||||
export const useSelectorCell = (rowId: string, fieldId: string) => {
|
||||
const database = useContext(DatabaseContext);
|
||||
const cells = useSnapshot(database.cells);
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
FieldSettingsChangesetPB,
|
||||
FieldVisibility,
|
||||
DatabaseViewIdPB,
|
||||
CreateFieldPosition,
|
||||
OrderObjectPositionTypePB,
|
||||
} from '@/services/backend';
|
||||
import {
|
||||
DatabaseEventDuplicateField,
|
||||
@ -90,7 +90,7 @@ export async function createField({
|
||||
}: {
|
||||
viewId: string;
|
||||
targetFieldId?: string;
|
||||
fieldPosition?: CreateFieldPosition;
|
||||
fieldPosition?: OrderObjectPositionTypePB;
|
||||
fieldType?: FieldType;
|
||||
data?: Uint8Array;
|
||||
}): Promise<Field> {
|
||||
@ -98,8 +98,10 @@ export async function createField({
|
||||
view_id: viewId,
|
||||
field_type: fieldType,
|
||||
type_option_data: data,
|
||||
target_field_id: targetFieldId,
|
||||
field_position: fieldPosition,
|
||||
field_position: {
|
||||
position: fieldPosition,
|
||||
object_id: targetFieldId,
|
||||
},
|
||||
});
|
||||
|
||||
const result = await DatabaseEventCreateField(payload);
|
||||
@ -157,12 +159,11 @@ export async function updateFieldType(viewId: string, fieldId: string, fieldType
|
||||
return result.unwrap();
|
||||
}
|
||||
|
||||
export async function moveField(viewId: string, fieldId: string, fromIndex: number, toIndex: number): Promise<void> {
|
||||
export async function moveField(viewId: string, fromFieldId: string, toFieldId: string): Promise<void> {
|
||||
const payload = MoveFieldPayloadPB.fromObject({
|
||||
view_id: viewId,
|
||||
field_id: fieldId,
|
||||
from_index: fromIndex,
|
||||
to_index: toIndex,
|
||||
from_field_id: fromFieldId,
|
||||
to_field_id: toFieldId,
|
||||
});
|
||||
|
||||
const result = await DatabaseEventMoveField(payload);
|
||||
|
@ -2,6 +2,7 @@ import {
|
||||
CreateRowPayloadPB,
|
||||
MoveGroupRowPayloadPB,
|
||||
MoveRowPayloadPB,
|
||||
OrderObjectPositionTypePB,
|
||||
RowIdPB,
|
||||
UpdateRowMetaChangesetPB,
|
||||
} from '@/services/backend';
|
||||
@ -17,13 +18,17 @@ import {
|
||||
import { pbToRowMeta, RowMeta } from './row_types';
|
||||
|
||||
export async function createRow(viewId: string, params?: {
|
||||
startRowId?: string;
|
||||
position?: OrderObjectPositionTypePB;
|
||||
rowId?: string;
|
||||
groupId?: string;
|
||||
data?: Record<string, string>;
|
||||
}): Promise<RowMeta> {
|
||||
const payload = CreateRowPayloadPB.fromObject({
|
||||
view_id: viewId,
|
||||
start_row_id: params?.startRowId,
|
||||
row_position: {
|
||||
position: params?.position,
|
||||
object_id: params?.rowId,
|
||||
},
|
||||
group_id: params?.groupId,
|
||||
data: params?.data ? { cell_data_by_field_id: params.data } : undefined,
|
||||
});
|
||||
|
@ -24,8 +24,8 @@ function Properties({ onItemClick }: PropertiesProps) {
|
||||
|
||||
const handleOnDragEnd = async (result: DropResult) => {
|
||||
const { destination, draggableId, source } = result;
|
||||
const newIndex = destination?.index;
|
||||
const oldIndex = source.index;
|
||||
const newIndex = destination?.index;
|
||||
|
||||
if (oldIndex === newIndex) {
|
||||
return;
|
||||
@ -35,11 +35,13 @@ function Properties({ onItemClick }: PropertiesProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newId = fields[newIndex ?? 0].id;
|
||||
|
||||
const newProperties = fieldService.reorderFields(fields as FieldType[], oldIndex, newIndex ?? 0);
|
||||
|
||||
setState(newProperties);
|
||||
|
||||
await fieldService.moveField(viewId, draggableId, oldIndex, newIndex);
|
||||
await fieldService.moveField(viewId, draggableId, newId ?? "");
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -44,23 +44,6 @@ function RecordProperties({ documentId, row }: Props) {
|
||||
setState(properties);
|
||||
}, [properties]);
|
||||
|
||||
// move the field in the database
|
||||
const onMoveProperty = useCallback(
|
||||
async (fieldId: string, prevId?: string) => {
|
||||
const fromIndex = fields.findIndex((field) => field.id === fieldId);
|
||||
|
||||
const prevIndex = prevId ? fields.findIndex((field) => field.id === prevId) : 0;
|
||||
const toIndex = prevIndex > fromIndex ? prevIndex : prevIndex + 1;
|
||||
|
||||
if (fromIndex === toIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
await fieldService.moveField(viewId, fieldId, fromIndex, toIndex);
|
||||
},
|
||||
[fields, viewId]
|
||||
);
|
||||
|
||||
// move the field in the state
|
||||
const handleOnDragEnd: OnDragEndResponder = useCallback(
|
||||
async (result: DropResult) => {
|
||||
@ -72,20 +55,16 @@ function RecordProperties({ documentId, row }: Props) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newId = properties[newIndex ?? 0].id;
|
||||
|
||||
// reorder the properties synchronously to avoid flickering
|
||||
const newProperties = fieldService.reorderFields(properties, oldIndex, newIndex ?? 0);
|
||||
|
||||
setState(newProperties);
|
||||
|
||||
// find the previous field id
|
||||
const prevIndex = newProperties.findIndex((field) => field.id === draggableId) - 1;
|
||||
const prevId = prevIndex >= 0 ? newProperties[prevIndex].id : undefined;
|
||||
|
||||
// update the order in the database.
|
||||
// why not prevIndex? because the properties was filtered, we need to use the previous id to find the correct index
|
||||
await onMoveProperty(draggableId, prevId);
|
||||
await fieldService.moveField(viewId, draggableId, newId);
|
||||
},
|
||||
[onMoveProperty, properties]
|
||||
[properties, viewId]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -8,7 +8,7 @@ import { ReactComponent as LeftSvg } from '$app/assets/left.svg';
|
||||
import { ReactComponent as RightSvg } from '$app/assets/right.svg';
|
||||
import { useViewId } from '$app/hooks';
|
||||
import { fieldService } from '$app/components/database/application';
|
||||
import { CreateFieldPosition, FieldVisibility } from '@/services/backend';
|
||||
import { OrderObjectPositionTypePB, FieldVisibility } from '@/services/backend';
|
||||
import { MenuItem } from '@mui/material';
|
||||
import ConfirmDialog from '$app/components/_shared/app-dialog/ConfirmDialog';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -83,7 +83,7 @@ function PropertyActions({ fieldId, onMenuItemClick, isPrimary, actions = defaul
|
||||
break;
|
||||
case FieldAction.InsertLeft:
|
||||
case FieldAction.InsertRight: {
|
||||
const fieldPosition = action === FieldAction.InsertLeft ? CreateFieldPosition.Before : CreateFieldPosition.After;
|
||||
const fieldPosition = action === FieldAction.InsertLeft ? OrderObjectPositionTypePB.Before : OrderObjectPositionTypePB.After;
|
||||
|
||||
const field = await fieldService.createField({
|
||||
viewId,
|
||||
|
@ -58,7 +58,6 @@ export const GridCell = memo(({ row, column, columnIndex, style, onEditRecord, g
|
||||
<GridNewRow
|
||||
getContainerRef={getContainerRef}
|
||||
index={columnIndex}
|
||||
startRowId={row.data.startRowId}
|
||||
groupId={row.data.groupId}
|
||||
/>
|
||||
</div>
|
||||
|
@ -4,7 +4,6 @@ import { throttle } from '$app/utils/tool';
|
||||
import { useViewId } from '$app/hooks';
|
||||
import { DragItem, DropPosition, DragType, useDraggable, useDroppable, ScrollDirection } from '../../_shared';
|
||||
import { fieldService, Field } from '../../application';
|
||||
import { useDatabase } from '../../Database.hooks';
|
||||
import { Property } from '$app/components/database/components/property';
|
||||
import GridResizer from '$app/components/database/grid/GridField/GridResizer';
|
||||
import GridFieldMenu from '$app/components/database/grid/GridField/GridFieldMenu';
|
||||
@ -23,7 +22,6 @@ export const GridField: FC<GridFieldProps> = memo(
|
||||
({ getScrollElement, resizeColumnWidth, onOpenMenu, onCloseMenu, field, ...props }) => {
|
||||
const menuOpened = useOpenMenu(field.id);
|
||||
const viewId = useViewId();
|
||||
const { fields } = useDatabase();
|
||||
const [openTooltip, setOpenTooltip] = useState(false);
|
||||
const [propertyMenuOpened, setPropertyMenuOpened] = useState(false);
|
||||
const [dropPosition, setDropPosition] = useState<DropPosition>(DropPosition.Before);
|
||||
@ -70,17 +68,14 @@ export const GridField: FC<GridFieldProps> = memo(
|
||||
const onDrop = useCallback(
|
||||
({ data }: DragItem) => {
|
||||
const dragField = data.field as Field;
|
||||
const fromIndex = fields.findIndex((item) => item.id === dragField.id);
|
||||
const dropIndex = fields.findIndex((item) => item.id === field.id);
|
||||
const toIndex = dropIndex + dropPosition + (fromIndex < dropIndex ? -1 : 0);
|
||||
|
||||
if (fromIndex === toIndex) {
|
||||
if (dragField.id === field.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
void fieldService.moveField(viewId, dragField.id, fromIndex, toIndex);
|
||||
void fieldService.moveField(viewId, dragField.id, field.id);
|
||||
},
|
||||
[viewId, field, fields, dropPosition]
|
||||
[viewId, field]
|
||||
);
|
||||
|
||||
const { isOver, listeners: dropListeners } = useDroppable({
|
||||
@ -92,9 +87,9 @@ export const GridField: FC<GridFieldProps> = memo(
|
||||
|
||||
const [menuAnchorPosition, setMenuAnchorPosition] = useState<
|
||||
| {
|
||||
top: number;
|
||||
left: number;
|
||||
}
|
||||
top: number;
|
||||
left: number;
|
||||
}
|
||||
| undefined
|
||||
>(undefined);
|
||||
|
||||
@ -168,9 +163,8 @@ export const GridField: FC<GridFieldProps> = memo(
|
||||
/>
|
||||
{isOver && (
|
||||
<div
|
||||
className={`absolute bottom-0 top-0 z-10 w-0.5 bg-blue-500 ${
|
||||
dropPosition === DropPosition.Before ? 'left-[-1px]' : 'left-full'
|
||||
}`}
|
||||
className={`absolute bottom-0 top-0 z-10 w-0.5 bg-blue-500 ${dropPosition === DropPosition.Before ? 'left-[-1px]' : 'left-full'
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
<GridResizer field={field} onWidthChange={resizeColumnWidth} />
|
||||
|
@ -6,22 +6,20 @@ import { ReactComponent as AddSvg } from '$app/assets/add.svg';
|
||||
|
||||
interface Props {
|
||||
index: number;
|
||||
startRowId?: string;
|
||||
groupId?: string;
|
||||
getContainerRef?: () => React.RefObject<HTMLDivElement>;
|
||||
}
|
||||
|
||||
const CSS_HIGHLIGHT_PROPERTY = 'bg-content-blue-50';
|
||||
|
||||
function GridNewRow({ index, startRowId, groupId, getContainerRef }: Props) {
|
||||
function GridNewRow({ index, groupId, getContainerRef }: Props) {
|
||||
const viewId = useViewId();
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
void rowService.createRow(viewId, {
|
||||
startRowId,
|
||||
groupId,
|
||||
});
|
||||
}, [viewId, groupId, startRowId]);
|
||||
}, [viewId, groupId]);
|
||||
|
||||
const toggleCssProperty = useCallback(
|
||||
(status: boolean) => {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useViewId } from '$app/hooks';
|
||||
import { useGetPrevRowId } from '$app/components/database';
|
||||
import { rowService } from '$app/components/database/application';
|
||||
import { autoScrollOnEdge, ScrollDirection } from '$app/components/database/_shared/dnd/utils';
|
||||
|
||||
@ -71,7 +70,6 @@ export function useDraggableGridRow(
|
||||
const dropRowIdRef = useRef<string | undefined>(undefined);
|
||||
const previewRef = useRef<HTMLDivElement | undefined>();
|
||||
const viewId = useViewId();
|
||||
const getPrevRowId = useGetPrevRowId();
|
||||
const onDragStart = useCallback(
|
||||
(e: React.DragEvent<HTMLButtonElement>) => {
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
@ -172,7 +170,7 @@ export function useDraggableGridRow(
|
||||
container.addEventListener('dragover', onDragOver);
|
||||
container.addEventListener('dragend', onDragEnd);
|
||||
container.addEventListener('drop', onDrop);
|
||||
}, [containerRef, getPrevRowId, isDragging, rowId, viewId]);
|
||||
}, [containerRef, isDragging, rowId, viewId]);
|
||||
|
||||
return {
|
||||
isDragging,
|
||||
|
@ -7,6 +7,7 @@ import { rowService } from '$app/components/database/application';
|
||||
import { useViewId } from '$app/hooks';
|
||||
import GridRowDragButton from '$app/components/database/grid/GridRowActions/GridRowDragButton';
|
||||
import GridRowMenu from '$app/components/database/grid/GridRowActions/GridRowMenu';
|
||||
import { OrderObjectPositionTypePB } from '@/services/backend';
|
||||
|
||||
function GridRowActions({
|
||||
rowId,
|
||||
@ -33,7 +34,8 @@ function GridRowActions({
|
||||
|
||||
const handleInsertRecordBelow = useCallback(() => {
|
||||
void rowService.createRow(viewId, {
|
||||
startRowId: rowId,
|
||||
position: OrderObjectPositionTypePB.After,
|
||||
rowId: rowId,
|
||||
});
|
||||
}, [viewId, rowId]);
|
||||
|
||||
|
@ -4,11 +4,11 @@ import { ReactComponent as AddSvg } from '$app/assets/add.svg';
|
||||
import { ReactComponent as DelSvg } from '$app/assets/delete.svg';
|
||||
import { ReactComponent as CopySvg } from '$app/assets/copy.svg';
|
||||
import Popover, { PopoverProps } from '@mui/material/Popover';
|
||||
import { useGetPrevRowId } from '$app/components/database';
|
||||
import { useViewId } from '$app/hooks';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { rowService } from '$app/components/database/application';
|
||||
import { Icon, MenuItem, MenuList } from '@mui/material';
|
||||
import { OrderObjectPositionTypePB } from '@/services/backend';
|
||||
|
||||
interface Option {
|
||||
label: string;
|
||||
@ -22,25 +22,23 @@ interface Props extends PopoverProps {
|
||||
}
|
||||
|
||||
function GridRowMenu({ rowId, ...props }: Props) {
|
||||
const getPrevRowId = useGetPrevRowId();
|
||||
|
||||
const viewId = useViewId();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleInsertRecordBelow = useCallback(() => {
|
||||
void rowService.createRow(viewId, {
|
||||
startRowId: rowId,
|
||||
position: OrderObjectPositionTypePB.After,
|
||||
rowId: rowId,
|
||||
});
|
||||
}, [viewId, rowId]);
|
||||
|
||||
const handleInsertRecordAbove = useCallback(() => {
|
||||
const prevRowId = getPrevRowId(rowId);
|
||||
|
||||
void rowService.createRow(viewId, {
|
||||
startRowId: prevRowId || undefined,
|
||||
position: OrderObjectPositionTypePB.Before,
|
||||
rowId: rowId,
|
||||
});
|
||||
}, [getPrevRowId, rowId, viewId]);
|
||||
}, [rowId, viewId]);
|
||||
|
||||
const handleDelRow = useCallback(() => {
|
||||
void rowService.deleteRow(viewId, rowId);
|
||||
|
@ -31,7 +31,6 @@ export interface CellRenderRow {
|
||||
export interface NewRenderRow {
|
||||
type: RenderRowType.NewRow;
|
||||
data: {
|
||||
startRowId?: string;
|
||||
groupId?: string;
|
||||
};
|
||||
}
|
||||
@ -66,9 +65,7 @@ export const rowMetasToRenderRow = (rowMetas: RowMeta[]): RenderRow[] => {
|
||||
})),
|
||||
{
|
||||
type: RenderRowType.NewRow,
|
||||
data: {
|
||||
startRowId: rowMetas.at(-1)?.id,
|
||||
},
|
||||
data: { },
|
||||
},
|
||||
{
|
||||
type: RenderRowType.CalculateRow,
|
||||
|
@ -349,11 +349,12 @@ async function testMoveField() {
|
||||
});
|
||||
|
||||
const fieldInfos = [...databaseController.fieldController.fieldInfos];
|
||||
const field_id = fieldInfos[0].field.id;
|
||||
const fromFieldId = fieldInfos[0].field.id;
|
||||
const toFieldId = fieldInfos[1].field.id;
|
||||
|
||||
await databaseController.moveField({ fieldId: field_id, fromIndex: 0, toIndex: 1 });
|
||||
await databaseController.moveField({ fromFieldId: fromFieldId, toFieldId: toFieldId });
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
assert(databaseController.fieldController.fieldInfos[1].field.id === field_id);
|
||||
assert(databaseController.fieldController.fieldInfos[1].field.id === fromFieldId);
|
||||
}
|
||||
|
||||
async function testGetSingleSelectFieldData() {
|
||||
|
@ -29,6 +29,7 @@ import {
|
||||
DatabaseViewIdPB,
|
||||
CreateRowPayloadPB,
|
||||
ViewIdPB,
|
||||
OrderObjectPositionTypePB,
|
||||
} from '@/services/backend';
|
||||
import { FolderEventCloseView } from '@/services/backend/events/flowy-folder2';
|
||||
import { TypeOptionController } from '$app/stores/effects/database/field/type_option/type_option_controller';
|
||||
@ -63,16 +64,19 @@ export class DatabaseBackendService {
|
||||
/// 2.The row will be placed after the passed-in rowId
|
||||
/// 3.The row will be moved to the group with groupId. Currently, grouping is
|
||||
/// only support in kanban board.
|
||||
createRow = async (params?: { rowId?: string; groupId?: string }) => {
|
||||
const payload = CreateRowPayloadPB.fromObject({ view_id: this.viewId });
|
||||
|
||||
if (params?.rowId !== undefined) {
|
||||
payload.start_row_id = params.rowId;
|
||||
}
|
||||
|
||||
if (params?.groupId !== undefined) {
|
||||
payload.group_id = params.groupId;
|
||||
}
|
||||
createRow = async (params?: {
|
||||
groupId?: string;
|
||||
position?: OrderObjectPositionTypePB;
|
||||
rowId?: string;
|
||||
}) => {
|
||||
const payload = CreateRowPayloadPB.fromObject({
|
||||
view_id: this.viewId,
|
||||
row_position: {
|
||||
position: params?.position,
|
||||
object_id: params?.rowId,
|
||||
},
|
||||
group_id: params?.groupId,
|
||||
});
|
||||
|
||||
return DatabaseEventCreateRow(payload);
|
||||
};
|
||||
@ -139,12 +143,11 @@ export class DatabaseBackendService {
|
||||
return DatabaseEventGetGroup(payload);
|
||||
};
|
||||
|
||||
moveField = (params: { fieldId: string; fromIndex: number; toIndex: number }) => {
|
||||
moveField = (params: { fromFieldId: string; toFieldId: string }) => {
|
||||
const payload = MoveFieldPayloadPB.fromObject({
|
||||
view_id: this.viewId,
|
||||
field_id: params.fieldId,
|
||||
from_index: params.fromIndex,
|
||||
to_index: params.toIndex,
|
||||
from_field_id: params.fromFieldId,
|
||||
to_field_id: params.toFieldId,
|
||||
});
|
||||
|
||||
return DatabaseEventMoveField(payload);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { DatabaseBackendService } from './database_bd_svc';
|
||||
import { FieldController, FieldInfo } from './field/field_controller';
|
||||
import { DatabaseViewCache } from './view/database_view_cache';
|
||||
import { DatabasePB, GroupPB, FlowyError } from '@/services/backend';
|
||||
import { DatabasePB, GroupPB, FlowyError, OrderObjectPositionTypePB } from '@/services/backend';
|
||||
import { RowChangedReason, RowInfo } from './row/row_cache';
|
||||
import { Err, Ok } from 'ts-results';
|
||||
import { DatabaseGroupController } from './group/group_controller';
|
||||
@ -108,7 +108,7 @@ export class DatabaseController {
|
||||
};
|
||||
|
||||
createRowAfter = (rowId: string) => {
|
||||
return this.backendService.createRow({ rowId });
|
||||
return this.backendService.createRow({ rowId, position: OrderObjectPositionTypePB.After });
|
||||
};
|
||||
|
||||
duplicateRow = async (rowId: string) => {
|
||||
@ -136,7 +136,7 @@ export class DatabaseController {
|
||||
return this.backendService.moveGroup(fromGroupId, toGroupId);
|
||||
};
|
||||
|
||||
moveField = (params: { fieldId: string; fromIndex: number; toIndex: number }) => {
|
||||
moveField = (params: { fromFieldId: string; toFieldId: string }) => {
|
||||
return this.backendService.moveField(params);
|
||||
};
|
||||
|
||||
@ -149,30 +149,28 @@ export class DatabaseController {
|
||||
};
|
||||
|
||||
addFieldToLeft = async (fieldId: string) => {
|
||||
const index = this.fieldController.fieldInfos.findIndex((fieldInfo) => fieldInfo.field.id === fieldId);
|
||||
|
||||
await this.backendService.createField();
|
||||
|
||||
const newFieldId = this.fieldController.fieldInfos[this.fieldController.fieldInfos.length - 1].field.id;
|
||||
|
||||
await this.moveField({
|
||||
fieldId: newFieldId,
|
||||
fromIndex: this.fieldController.fieldInfos.length - 1,
|
||||
toIndex: index,
|
||||
fromFieldId: newFieldId,
|
||||
toFieldId: fieldId,
|
||||
});
|
||||
};
|
||||
|
||||
addFieldToRight = async (fieldId: string) => {
|
||||
const index = this.fieldController.fieldInfos.findIndex((fieldInfo) => fieldInfo.field.id === fieldId);
|
||||
|
||||
await this.backendService.createField();
|
||||
|
||||
const newFieldId = this.fieldController.fieldInfos[this.fieldController.fieldInfos.length - 1].field.id;
|
||||
|
||||
const index = this.fieldController.fieldInfos.findIndex((fieldInfo) => fieldInfo.field.id === fieldId);
|
||||
|
||||
const toFieldId = this.fieldController.fieldInfos[index + 1].field.id;
|
||||
|
||||
await this.moveField({
|
||||
fieldId: newFieldId,
|
||||
fromIndex: this.fieldController.fieldInfos.length - 1,
|
||||
toIndex: index + 1,
|
||||
fromFieldId: newFieldId,
|
||||
toFieldId: toFieldId,
|
||||
});
|
||||
};
|
||||
|
||||
|
40
frontend/rust-lib/Cargo.lock
generated
40
frontend/rust-lib/Cargo.lock
generated
@ -733,7 +733,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=32218f1b6a9b09a9bbaa1835749e016246c092d4#32218f1b6a9b09a9bbaa1835749e016246c092d4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ac2ab049e367009b59c2e411c28580609a19e8b8#ac2ab049e367009b59c2e411c28580609a19e8b8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -752,7 +752,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-database"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=32218f1b6a9b09a9bbaa1835749e016246c092d4#32218f1b6a9b09a9bbaa1835749e016246c092d4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ac2ab049e367009b59c2e411c28580609a19e8b8#ac2ab049e367009b59c2e411c28580609a19e8b8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -782,7 +782,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-derive"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=32218f1b6a9b09a9bbaa1835749e016246c092d4#32218f1b6a9b09a9bbaa1835749e016246c092d4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ac2ab049e367009b59c2e411c28580609a19e8b8#ac2ab049e367009b59c2e411c28580609a19e8b8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -794,7 +794,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-document"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=32218f1b6a9b09a9bbaa1835749e016246c092d4#32218f1b6a9b09a9bbaa1835749e016246c092d4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ac2ab049e367009b59c2e411c28580609a19e8b8#ac2ab049e367009b59c2e411c28580609a19e8b8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@ -813,7 +813,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=32218f1b6a9b09a9bbaa1835749e016246c092d4#32218f1b6a9b09a9bbaa1835749e016246c092d4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ac2ab049e367009b59c2e411c28580609a19e8b8#ac2ab049e367009b59c2e411c28580609a19e8b8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -827,7 +827,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-folder"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=32218f1b6a9b09a9bbaa1835749e016246c092d4#32218f1b6a9b09a9bbaa1835749e016246c092d4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ac2ab049e367009b59c2e411c28580609a19e8b8#ac2ab049e367009b59c2e411c28580609a19e8b8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@ -869,7 +869,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-persistence"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=32218f1b6a9b09a9bbaa1835749e016246c092d4#32218f1b6a9b09a9bbaa1835749e016246c092d4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ac2ab049e367009b59c2e411c28580609a19e8b8#ac2ab049e367009b59c2e411c28580609a19e8b8"
|
||||
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=32218f1b6a9b09a9bbaa1835749e016246c092d4#32218f1b6a9b09a9bbaa1835749e016246c092d4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ac2ab049e367009b59c2e411c28580609a19e8b8#ac2ab049e367009b59c2e411c28580609a19e8b8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -916,7 +916,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-user"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=32218f1b6a9b09a9bbaa1835749e016246c092d4#32218f1b6a9b09a9bbaa1835749e016246c092d4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ac2ab049e367009b59c2e411c28580609a19e8b8#ac2ab049e367009b59c2e411c28580609a19e8b8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@ -1149,7 +1149,7 @@ dependencies = [
|
||||
"cssparser-macros",
|
||||
"dtoa-short",
|
||||
"itoa",
|
||||
"phf 0.8.0",
|
||||
"phf 0.11.2",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
@ -3653,7 +3653,7 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||
dependencies = [
|
||||
"phf_macros",
|
||||
"phf_macros 0.8.0",
|
||||
"phf_shared 0.8.0",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
@ -3673,6 +3673,7 @@ version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||
dependencies = [
|
||||
"phf_macros 0.11.2",
|
||||
"phf_shared 0.11.2",
|
||||
]
|
||||
|
||||
@ -3740,6 +3741,19 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
|
||||
dependencies = [
|
||||
"phf_generator 0.11.2",
|
||||
"phf_shared 0.11.2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.31",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
@ -3943,7 +3957,7 @@ checksum = "8bdf592881d821b83d471f8af290226c8d51402259e9bb5be7f9f8bdebbb11ac"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"heck 0.4.1",
|
||||
"itertools 0.10.5",
|
||||
"itertools 0.11.0",
|
||||
"log",
|
||||
"multimap",
|
||||
"once_cell",
|
||||
@ -3964,7 +3978,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.10.5",
|
||||
"itertools 0.11.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.31",
|
||||
|
@ -109,11 +109,11 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "9da
|
||||
# To switch to the local path, run:
|
||||
# scripts/tool/update_collab_source.sh
|
||||
# ⚠️⚠️⚠️️
|
||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "32218f1b6a9b09a9bbaa1835749e016246c092d4" }
|
||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "32218f1b6a9b09a9bbaa1835749e016246c092d4" }
|
||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "32218f1b6a9b09a9bbaa1835749e016246c092d4" }
|
||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "32218f1b6a9b09a9bbaa1835749e016246c092d4" }
|
||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "32218f1b6a9b09a9bbaa1835749e016246c092d4" }
|
||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "32218f1b6a9b09a9bbaa1835749e016246c092d4" }
|
||||
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "32218f1b6a9b09a9bbaa1835749e016246c092d4" }
|
||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "32218f1b6a9b09a9bbaa1835749e016246c092d4" }
|
||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ac2ab049e367009b59c2e411c28580609a19e8b8" }
|
||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ac2ab049e367009b59c2e411c28580609a19e8b8" }
|
||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ac2ab049e367009b59c2e411c28580609a19e8b8" }
|
||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ac2ab049e367009b59c2e411c28580609a19e8b8" }
|
||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ac2ab049e367009b59c2e411c28580609a19e8b8" }
|
||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ac2ab049e367009b59c2e411c28580609a19e8b8" }
|
||||
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ac2ab049e367009b59c2e411c28580609a19e8b8" }
|
||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ac2ab049e367009b59c2e411c28580609a19e8b8" }
|
||||
|
@ -188,14 +188,14 @@ impl EventIntegrationTest {
|
||||
pub async fn create_row(
|
||||
&self,
|
||||
view_id: &str,
|
||||
start_row_id: Option<String>,
|
||||
row_position: OrderObjectPositionPB,
|
||||
data: Option<RowDataPB>,
|
||||
) -> RowMetaPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::CreateRow)
|
||||
.payload(CreateRowPayloadPB {
|
||||
view_id: view_id.to_string(),
|
||||
start_row_id,
|
||||
row_position,
|
||||
group_id: None,
|
||||
data,
|
||||
})
|
||||
|
@ -6,8 +6,8 @@ use event_integration::event_builder::EventBuilder;
|
||||
use event_integration::EventIntegrationTest;
|
||||
use flowy_database2::entities::{
|
||||
CellChangesetPB, CellIdPB, ChecklistCellDataChangesetPB, DatabaseLayoutPB,
|
||||
DatabaseSettingChangesetPB, DatabaseViewIdPB, DateChangesetPB, FieldType, SelectOptionCellDataPB,
|
||||
UpdateRowMetaChangesetPB,
|
||||
DatabaseSettingChangesetPB, DatabaseViewIdPB, DateChangesetPB, FieldType, OrderObjectPositionPB,
|
||||
SelectOptionCellDataPB, UpdateRowMetaChangesetPB,
|
||||
};
|
||||
use lib_infra::util::timestamp;
|
||||
|
||||
@ -202,7 +202,9 @@ async fn create_row_event_test() {
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
.await;
|
||||
|
||||
let _ = test.create_row(&grid_view.id, None, None).await;
|
||||
let _ = test
|
||||
.create_row(&grid_view.id, OrderObjectPositionPB::default(), None)
|
||||
.await;
|
||||
let database = test.get_database(&grid_view.id).await;
|
||||
assert_eq!(database.rows.len(), 4);
|
||||
}
|
||||
@ -755,7 +757,9 @@ async fn create_calendar_event_test() {
|
||||
.unwrap();
|
||||
|
||||
// create a new row
|
||||
let row = test.create_row(&calendar_view.id, None, None).await;
|
||||
let row = test
|
||||
.create_row(&calendar_view.id, OrderObjectPositionPB::default(), None)
|
||||
.await;
|
||||
|
||||
// Insert data into the date cell of the first row.
|
||||
let error = test
|
||||
|
@ -84,21 +84,17 @@ pub struct MoveFieldPayloadPB {
|
||||
pub view_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub field_id: String,
|
||||
pub from_field_id: String,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub from_index: i32,
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub to_index: i32,
|
||||
pub to_field_id: String,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MoveFieldParams {
|
||||
pub view_id: String,
|
||||
pub field_id: String,
|
||||
pub from_index: i32,
|
||||
pub to_index: i32,
|
||||
pub from_field_id: String,
|
||||
pub to_field_id: String,
|
||||
}
|
||||
|
||||
impl TryInto<MoveFieldParams> for MoveFieldPayloadPB {
|
||||
@ -106,12 +102,13 @@ impl TryInto<MoveFieldParams> for MoveFieldPayloadPB {
|
||||
|
||||
fn try_into(self) -> Result<MoveFieldParams, Self::Error> {
|
||||
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseViewIdIsEmpty)?;
|
||||
let item_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::InvalidParams)?;
|
||||
let from_field_id =
|
||||
NotEmptyStr::parse(self.from_field_id).map_err(|_| ErrorCode::InvalidParams)?;
|
||||
let to_field_id = NotEmptyStr::parse(self.to_field_id).map_err(|_| ErrorCode::InvalidParams)?;
|
||||
Ok(MoveFieldParams {
|
||||
view_id: view_id.0,
|
||||
field_id: item_id.0,
|
||||
from_index: self.from_index,
|
||||
to_index: self.to_index,
|
||||
from_field_id: from_field_id.0,
|
||||
to_field_id: to_field_id.0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use flowy_error::ErrorCode;
|
||||
|
||||
use crate::entities::parser::NotEmptyStr;
|
||||
use crate::entities::position_entities::OrderObjectPositionPB;
|
||||
use crate::impl_into_field_type;
|
||||
|
||||
/// [FieldPB] defines a Field's attributes. Such as the name, field_type, and width. etc.
|
||||
@ -164,20 +165,7 @@ pub struct CreateFieldPayloadPB {
|
||||
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,
|
||||
pub field_position: OrderObjectPositionPB,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -204,34 +192,7 @@ impl TryInto<CreateFieldParams> for CreateFieldPayloadPB {
|
||||
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(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(ErrorCode::InvalidParams)?;
|
||||
let field_id = NotEmptyStr::parse(field_id)
|
||||
.map_err(|_| ErrorCode::InvalidParams)?
|
||||
.0;
|
||||
OrderObjectPosition::After(field_id)
|
||||
},
|
||||
};
|
||||
let position = self.field_position.try_into()?;
|
||||
|
||||
Ok(CreateFieldParams {
|
||||
view_id: view_id.0,
|
||||
|
@ -7,15 +7,16 @@ mod field_settings_entities;
|
||||
pub mod filter_entities;
|
||||
mod group_entities;
|
||||
pub mod parser;
|
||||
mod position_entities;
|
||||
mod row_entities;
|
||||
pub mod setting_entities;
|
||||
mod share_entities;
|
||||
mod sort_entities;
|
||||
mod type_option_entities;
|
||||
mod view_entities;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod share_entities;
|
||||
mod type_option_entities;
|
||||
|
||||
pub use board_entities::*;
|
||||
pub use calendar_entities::*;
|
||||
@ -25,6 +26,7 @@ pub use field_entities::*;
|
||||
pub use field_settings_entities::*;
|
||||
pub use filter_entities::*;
|
||||
pub use group_entities::*;
|
||||
pub use position_entities::*;
|
||||
pub use row_entities::*;
|
||||
pub use setting_entities::*;
|
||||
pub use share_entities::*;
|
||||
|
@ -0,0 +1,89 @@
|
||||
use collab_database::views::OrderObjectPosition;
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use flowy_error::ErrorCode;
|
||||
|
||||
use crate::entities::parser::NotEmptyStr;
|
||||
|
||||
#[derive(Debug, Default, ProtoBuf)]
|
||||
pub struct OrderObjectPositionPB {
|
||||
#[pb(index = 1)]
|
||||
pub position: OrderObjectPositionTypePB,
|
||||
|
||||
#[pb(index = 2, one_of)]
|
||||
pub object_id: Option<String>,
|
||||
}
|
||||
|
||||
impl OrderObjectPositionPB {
|
||||
pub fn start() -> Self {
|
||||
Self {
|
||||
position: OrderObjectPositionTypePB::Start,
|
||||
object_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end() -> Self {
|
||||
Self {
|
||||
position: OrderObjectPositionTypePB::End,
|
||||
object_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn before(object_id: String) -> Self {
|
||||
Self {
|
||||
position: OrderObjectPositionTypePB::Before,
|
||||
object_id: Some(object_id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn after(object_id: String) -> Self {
|
||||
Self {
|
||||
position: OrderObjectPositionTypePB::After,
|
||||
object_id: Some(object_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, ProtoBuf_Enum)]
|
||||
#[repr(u8)]
|
||||
pub enum OrderObjectPositionTypePB {
|
||||
#[default]
|
||||
End = 0,
|
||||
Start = 1,
|
||||
Before = 2,
|
||||
After = 3,
|
||||
}
|
||||
|
||||
impl TryFrom<OrderObjectPositionPB> for OrderObjectPosition {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_from(value: OrderObjectPositionPB) -> Result<Self, Self::Error> {
|
||||
match value.position {
|
||||
OrderObjectPositionTypePB::Start => {
|
||||
if value.object_id.is_some() {
|
||||
return Err(ErrorCode::InvalidParams);
|
||||
}
|
||||
Ok(OrderObjectPosition::Start)
|
||||
},
|
||||
OrderObjectPositionTypePB::End => {
|
||||
if value.object_id.is_some() {
|
||||
return Err(ErrorCode::InvalidParams);
|
||||
}
|
||||
Ok(OrderObjectPosition::End)
|
||||
},
|
||||
OrderObjectPositionTypePB::Before => {
|
||||
let field_id = value.object_id.ok_or(ErrorCode::InvalidParams)?;
|
||||
let field_id = NotEmptyStr::parse(field_id)
|
||||
.map_err(|_| ErrorCode::InvalidParams)?
|
||||
.0;
|
||||
Ok(OrderObjectPosition::Before(field_id))
|
||||
},
|
||||
OrderObjectPositionTypePB::After => {
|
||||
let field_id = value.object_id.ok_or(ErrorCode::InvalidParams)?;
|
||||
let field_id = NotEmptyStr::parse(field_id)
|
||||
.map_err(|_| ErrorCode::InvalidParams)?
|
||||
.0;
|
||||
Ok(OrderObjectPosition::After(field_id))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use collab_database::rows::{Row, RowDetail, RowId};
|
||||
use collab_database::views::RowOrder;
|
||||
use collab_database::views::{OrderObjectPosition, RowOrder};
|
||||
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::ErrorCode;
|
||||
|
||||
use crate::entities::parser::NotEmptyStr;
|
||||
use crate::entities::position_entities::OrderObjectPositionPB;
|
||||
use crate::services::database::{InsertedRow, UpdatedRow};
|
||||
|
||||
/// [RowPB] Describes a row. Has the id of the parent Block. Has the metadata of the row.
|
||||
@ -339,8 +340,8 @@ pub struct CreateRowPayloadPB {
|
||||
#[pb(index = 1)]
|
||||
pub view_id: String,
|
||||
|
||||
#[pb(index = 2, one_of)]
|
||||
pub start_row_id: Option<String>,
|
||||
#[pb(index = 2)]
|
||||
pub row_position: OrderObjectPositionPB,
|
||||
|
||||
#[pb(index = 3, one_of)]
|
||||
pub group_id: Option<String>,
|
||||
@ -358,7 +359,7 @@ pub struct RowDataPB {
|
||||
#[derive(Default)]
|
||||
pub struct CreateRowParams {
|
||||
pub view_id: String,
|
||||
pub start_row_id: Option<RowId>,
|
||||
pub row_position: OrderObjectPosition,
|
||||
pub group_id: Option<String>,
|
||||
pub cell_data_by_field_id: Option<HashMap<String, String>>,
|
||||
}
|
||||
@ -368,10 +369,10 @@ impl TryInto<CreateRowParams> for CreateRowPayloadPB {
|
||||
|
||||
fn try_into(self) -> Result<CreateRowParams, Self::Error> {
|
||||
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::ViewIdIsInvalid)?;
|
||||
let start_row_id = self.start_row_id.map(RowId::from);
|
||||
let position = self.row_position.try_into()?;
|
||||
Ok(CreateRowParams {
|
||||
view_id: view_id.0,
|
||||
start_row_id,
|
||||
row_position: position,
|
||||
group_id: self.group_id,
|
||||
cell_data_by_field_id: self.data.map(|data| data.cell_data_by_field_id),
|
||||
})
|
||||
|
@ -2,7 +2,6 @@ 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};
|
||||
@ -345,14 +344,7 @@ pub(crate) async fn move_field_handler(
|
||||
let manager = upgrade_manager(manager)?;
|
||||
let params: MoveFieldParams = data.into_inner().try_into()?;
|
||||
let database_editor = manager.get_database_with_view_id(¶ms.view_id).await?;
|
||||
database_editor
|
||||
.move_field(
|
||||
¶ms.view_id,
|
||||
¶ms.field_id,
|
||||
params.from_index,
|
||||
params.to_index,
|
||||
)
|
||||
.await?;
|
||||
database_editor.move_field(params).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -416,7 +408,7 @@ pub(crate) async fn duplicate_row_handler(
|
||||
let params: RowIdParams = data.into_inner().try_into()?;
|
||||
let database_editor = manager.get_database_with_view_id(¶ms.view_id).await?;
|
||||
database_editor
|
||||
.duplicate_row(¶ms.view_id, params.group_id, ¶ms.row_id)
|
||||
.duplicate_row(¶ms.view_id, ¶ms.row_id)
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
@ -431,7 +423,7 @@ pub(crate) async fn move_row_handler(
|
||||
let database_editor = manager.get_database_with_view_id(¶ms.view_id).await?;
|
||||
database_editor
|
||||
.move_row(¶ms.view_id, params.from_row_id, params.to_row_id)
|
||||
.await;
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -448,16 +440,12 @@ 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,
|
||||
row_position: position,
|
||||
row_position: params.row_position,
|
||||
timestamp: timestamp(),
|
||||
};
|
||||
match database_editor
|
||||
|
@ -168,8 +168,9 @@ pub enum DatabaseEvent {
|
||||
#[event(input = "DuplicateFieldPayloadPB")]
|
||||
DuplicateField = 21,
|
||||
|
||||
/// [MoveItem] event is used to move an item. For the moment, Item has two types defined in
|
||||
/// [MoveItemTypePB].
|
||||
/// [MoveFieldPB] event is used to reorder a field in a view. The
|
||||
/// [MoveFieldPayloadPB] contains the `field_id` of the moved field and its
|
||||
/// new position.
|
||||
#[event(input = "MoveFieldPayloadPB")]
|
||||
MoveField = 22,
|
||||
|
||||
|
@ -402,37 +402,47 @@ impl DatabaseEditor {
|
||||
}
|
||||
|
||||
// consider returning a result. But most of the time, it should be fine to just ignore the error.
|
||||
pub async fn duplicate_row(&self, view_id: &str, group_id: Option<String>, row_id: &RowId) {
|
||||
pub async fn duplicate_row(&self, view_id: &str, row_id: &RowId) {
|
||||
let params = self.database.lock().duplicate_row(row_id);
|
||||
match params {
|
||||
None => {
|
||||
warn!("Failed to duplicate row: {}", row_id);
|
||||
},
|
||||
None => warn!("Failed to duplicate row: {}", row_id),
|
||||
Some(params) => {
|
||||
let _ = self.create_row(view_id, group_id, params).await;
|
||||
let _ = self.create_row(view_id, None, params).await;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn move_row(&self, view_id: &str, from: RowId, to: RowId) {
|
||||
pub async fn move_row(
|
||||
&self,
|
||||
view_id: &str,
|
||||
from_row_id: RowId,
|
||||
to_row_id: RowId,
|
||||
) -> FlowyResult<()> {
|
||||
let database = self.database.lock();
|
||||
if let (Some(row_detail), Some(from_index), Some(to_index)) = (
|
||||
database.get_row_detail(&from),
|
||||
database.index_of_row(view_id, &from),
|
||||
database.index_of_row(view_id, &to),
|
||||
) {
|
||||
database.views.update_database_view(view_id, |view| {
|
||||
view.move_row_order(from_index as u32, to_index as u32);
|
||||
});
|
||||
drop(database);
|
||||
|
||||
let delete_row_id = from.into_inner();
|
||||
let insert_row = InsertedRowPB::new(RowMetaPB::from(row_detail)).with_index(to_index as i32);
|
||||
let row_detail = database.get_row_detail(&from_row_id).ok_or_else(|| {
|
||||
let msg = format!("Cannot find row {}", from_row_id);
|
||||
FlowyError::internal().with_context(msg)
|
||||
})?;
|
||||
|
||||
database.views.update_database_view(view_id, |view| {
|
||||
view.move_row_order(&from_row_id, &to_row_id);
|
||||
});
|
||||
|
||||
let new_index = database.index_of_row(view_id, &from_row_id);
|
||||
drop(database);
|
||||
|
||||
if let Some(index) = new_index {
|
||||
let delete_row_id = from_row_id.into_inner();
|
||||
let insert_row = InsertedRowPB::new(RowMetaPB::from(row_detail)).with_index(index as i32);
|
||||
let changes = RowsChangePB::from_move(vec![delete_row_id], vec![insert_row]);
|
||||
|
||||
send_notification(view_id, DatabaseNotification::DidUpdateViewRows)
|
||||
.payload(changes)
|
||||
.send();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn create_row(
|
||||
@ -504,28 +514,34 @@ impl DatabaseEditor {
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn move_field(
|
||||
&self,
|
||||
view_id: &str,
|
||||
field_id: &str,
|
||||
from: i32,
|
||||
to: i32,
|
||||
) -> FlowyResult<()> {
|
||||
let (database_id, field) = {
|
||||
pub async fn move_field(&self, params: MoveFieldParams) -> FlowyResult<()> {
|
||||
let (field, new_index) = {
|
||||
let database = self.database.lock();
|
||||
database.views.update_database_view(view_id, |view_update| {
|
||||
view_update.move_field_order(from as u32, to as u32);
|
||||
});
|
||||
let field = database.fields.get_field(field_id);
|
||||
let database_id = database.get_database_id();
|
||||
(database_id, field)
|
||||
|
||||
let field = database
|
||||
.fields
|
||||
.get_field(¶ms.from_field_id)
|
||||
.ok_or_else(|| {
|
||||
let msg = format!("Field with id: {} not found", ¶ms.from_field_id);
|
||||
FlowyError::internal().with_context(msg)
|
||||
})?;
|
||||
|
||||
database
|
||||
.views
|
||||
.update_database_view(¶ms.view_id, |view_update| {
|
||||
view_update.move_field_order(¶ms.from_field_id, ¶ms.to_field_id);
|
||||
});
|
||||
|
||||
let new_index = database.index_of_field(¶ms.view_id, ¶ms.from_field_id);
|
||||
|
||||
(field, new_index)
|
||||
};
|
||||
|
||||
if let Some(field) = field {
|
||||
let delete_field = FieldIdPB::from(field_id);
|
||||
let insert_field = IndexFieldPB::from_field(field, to as usize);
|
||||
if let Some(index) = new_index {
|
||||
let delete_field = FieldIdPB::from(params.from_field_id);
|
||||
let insert_field = IndexFieldPB::from_field(field, index);
|
||||
let notified_changeset = DatabaseFieldChangesetPB {
|
||||
view_id: database_id,
|
||||
view_id: params.view_id,
|
||||
inserted_fields: vec![insert_field],
|
||||
deleted_fields: vec![delete_field],
|
||||
updated_fields: vec![],
|
||||
@ -533,6 +549,7 @@ impl DatabaseEditor {
|
||||
|
||||
self.notify_did_update_database(notified_changeset).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -955,7 +972,7 @@ impl DatabaseEditor {
|
||||
.map(|row_detail| row_detail.row.id.clone())
|
||||
};
|
||||
if let Some(row_id) = to_row.clone() {
|
||||
self.move_row(view_id, from_row.clone(), row_id).await;
|
||||
self.move_row(view_id, from_row.clone(), row_id).await?;
|
||||
}
|
||||
|
||||
if from_group == to_group {
|
||||
|
Loading…
Reference in New Issue
Block a user