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