feat: support edit imported database (#6061)

* chore: change field type of imported csv

* fix: support load 10000 rows

* fix: clippy
This commit is contained in:
Nathan.fooo 2024-08-25 14:28:51 +08:00 committed by GitHub
parent d3b7c5fea5
commit a487aa74fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
68 changed files with 566 additions and 503 deletions

View File

@ -44,7 +44,8 @@ class RowCache {
for (final fieldInfo in fieldInfos) { for (final fieldInfo in fieldInfos) {
_cellMemCache.removeCellWithFieldId(fieldInfo.id); _cellMemCache.removeCellWithFieldId(fieldInfo.id);
} }
_changedNotifier.receive(const ChangedReason.fieldDidChange());
_changedNotifier?.receive(const ChangedReason.fieldDidChange());
}); });
} }
@ -53,7 +54,7 @@ class RowCache {
final CellMemCache _cellMemCache; final CellMemCache _cellMemCache;
final RowLifeCycle _rowLifeCycle; final RowLifeCycle _rowLifeCycle;
final RowFieldsDelegate _fieldDelegate; final RowFieldsDelegate _fieldDelegate;
final RowChangesetNotifier _changedNotifier; RowChangesetNotifier? _changedNotifier;
/// Returns a unmodifiable list of RowInfo /// Returns a unmodifiable list of RowInfo
UnmodifiableListView<RowInfo> get rowInfos { UnmodifiableListView<RowInfo> get rowInfos {
@ -67,7 +68,8 @@ class RowCache {
} }
CellMemCache get cellCache => _cellMemCache; CellMemCache get cellCache => _cellMemCache;
ChangedReason get changeReason => _changedNotifier.reason; ChangedReason get changeReason =>
_changedNotifier?.reason ?? const InitialListState();
RowInfo? getRow(RowId rowId) { RowInfo? getRow(RowId rowId) {
return _rowList.get(rowId); return _rowList.get(rowId);
@ -78,18 +80,19 @@ class RowCache {
final rowInfo = buildGridRow(row); final rowInfo = buildGridRow(row);
_rowList.add(rowInfo); _rowList.add(rowInfo);
} }
_changedNotifier.receive(const ChangedReason.setInitialRows()); _changedNotifier?.receive(const ChangedReason.setInitialRows());
} }
void setRowMeta(RowMetaPB rowMeta) { void setRowMeta(RowMetaPB rowMeta) {
final rowInfo = buildGridRow(rowMeta); final rowInfo = buildGridRow(rowMeta);
_rowList.add(rowInfo); _rowList.add(rowInfo);
_changedNotifier.receive(const ChangedReason.didFetchRow()); _changedNotifier?.receive(const ChangedReason.didFetchRow());
} }
void dispose() { void dispose() {
_rowLifeCycle.onRowDisposed(); _rowLifeCycle.onRowDisposed();
_changedNotifier.dispose(); _changedNotifier?.dispose();
_changedNotifier = null;
_cellMemCache.dispose(); _cellMemCache.dispose();
} }
@ -106,7 +109,7 @@ class RowCache {
void reorderAllRows(List<String> rowIds) { void reorderAllRows(List<String> rowIds) {
_rowList.reorderWithRowIds(rowIds); _rowList.reorderWithRowIds(rowIds);
_changedNotifier.receive(const ChangedReason.reorderRows()); _changedNotifier?.receive(const ChangedReason.reorderRows());
} }
void reorderSingleRow(ReorderSingleRowPB reorderRow) { void reorderSingleRow(ReorderSingleRowPB reorderRow) {
@ -117,7 +120,7 @@ class RowCache {
reorderRow.oldIndex, reorderRow.oldIndex,
reorderRow.newIndex, reorderRow.newIndex,
); );
_changedNotifier.receive( _changedNotifier?.receive(
ChangedReason.reorderSingleRow( ChangedReason.reorderSingleRow(
reorderRow, reorderRow,
rowInfo, rowInfo,
@ -130,7 +133,7 @@ class RowCache {
for (final rowId in deletedRowIds) { for (final rowId in deletedRowIds) {
final deletedRow = _rowList.remove(rowId); final deletedRow = _rowList.remove(rowId);
if (deletedRow != null) { if (deletedRow != null) {
_changedNotifier.receive(ChangedReason.delete(deletedRow)); _changedNotifier?.receive(ChangedReason.delete(deletedRow));
} }
} }
} }
@ -140,7 +143,7 @@ class RowCache {
final insertedIndex = final insertedIndex =
_rowList.insert(insertedRow.index, buildGridRow(insertedRow.rowMeta)); _rowList.insert(insertedRow.index, buildGridRow(insertedRow.rowMeta));
if (insertedIndex != null) { if (insertedIndex != null) {
_changedNotifier.receive(ChangedReason.insert(insertedIndex)); _changedNotifier?.receive(ChangedReason.insert(insertedIndex));
} }
} }
} }
@ -165,7 +168,7 @@ class RowCache {
_rowList.updateRows(updatedList, (rowId) => buildGridRow(rowId)); _rowList.updateRows(updatedList, (rowId) => buildGridRow(rowId));
if (updatedIndexs.isNotEmpty) { if (updatedIndexs.isNotEmpty) {
_changedNotifier.receive(ChangedReason.update(updatedIndexs)); _changedNotifier?.receive(ChangedReason.update(updatedIndexs));
} }
} }
@ -173,7 +176,7 @@ class RowCache {
for (final rowId in invisibleRows) { for (final rowId in invisibleRows) {
final deletedRow = _rowList.remove(rowId); final deletedRow = _rowList.remove(rowId);
if (deletedRow != null) { if (deletedRow != null) {
_changedNotifier.receive(ChangedReason.delete(deletedRow)); _changedNotifier?.receive(ChangedReason.delete(deletedRow));
} }
} }
} }
@ -183,14 +186,16 @@ class RowCache {
final insertedIndex = final insertedIndex =
_rowList.insert(insertedRow.index, buildGridRow(insertedRow.rowMeta)); _rowList.insert(insertedRow.index, buildGridRow(insertedRow.rowMeta));
if (insertedIndex != null) { if (insertedIndex != null) {
_changedNotifier.receive(ChangedReason.insert(insertedIndex)); _changedNotifier?.receive(ChangedReason.insert(insertedIndex));
} }
} }
} }
void onRowsChanged(void Function(ChangedReason) onRowChanged) { void onRowsChanged(void Function(ChangedReason) onRowChanged) {
_changedNotifier.addListener(() { _changedNotifier?.addListener(() {
onRowChanged(_changedNotifier.reason); if (_changedNotifier != null) {
onRowChanged(_changedNotifier!.reason);
}
}); });
} }
@ -203,17 +208,19 @@ class RowCache {
final rowInfo = _rowList.get(rowId); final rowInfo = _rowList.get(rowId);
if (rowInfo != null) { if (rowInfo != null) {
final cellDataMap = _makeCells(rowInfo.rowMeta); final cellDataMap = _makeCells(rowInfo.rowMeta);
onRowChanged(cellDataMap, _changedNotifier.reason); if (_changedNotifier != null) {
onRowChanged(cellDataMap, _changedNotifier!.reason);
}
} }
} }
} }
_changedNotifier.addListener(listenerHandler); _changedNotifier?.addListener(listenerHandler);
return listenerHandler; return listenerHandler;
} }
void removeRowListener(VoidCallback callback) { void removeRowListener(VoidCallback callback) {
_changedNotifier.removeListener(callback); _changedNotifier?.removeListener(callback);
} }
List<CellContext> loadCells(RowMetaPB rowMeta) { List<CellContext> loadCells(RowMetaPB rowMeta) {
@ -242,7 +249,7 @@ class RowCache {
rowId: rowMetaPB.id, rowId: rowMetaPB.id,
); );
_changedNotifier.receive(ChangedReason.update(updatedIndexs)); _changedNotifier?.receive(ChangedReason.update(updatedIndexs));
} }
}, },
(err) => Log.error(err), (err) => Log.error(err),

View File

@ -18,19 +18,7 @@ class RowController {
}) : _rowMeta = rowMeta, }) : _rowMeta = rowMeta,
_rowCache = rowCache, _rowCache = rowCache,
_rowBackendSvc = RowBackendService(viewId: viewId), _rowBackendSvc = RowBackendService(viewId: viewId),
_rowListener = RowListener(rowMeta.id) { _rowListener = RowListener(rowMeta.id);
_rowBackendSvc.initRow(rowMeta.id);
_rowListener.start(
onMetaChanged: (newRowMeta) {
if (_isDisposed) {
return;
}
_rowMeta = newRowMeta;
_rowCache.setRowMeta(newRowMeta);
_onRowMetaChanged?.call();
},
);
}
RowMetaPB _rowMeta; RowMetaPB _rowMeta;
final String? groupId; final String? groupId;
@ -42,13 +30,26 @@ class RowController {
final RowBackendService _rowBackendSvc; final RowBackendService _rowBackendSvc;
bool _isDisposed = false; bool _isDisposed = false;
CellMemCache get cellCache => _rowCache.cellCache;
String get rowId => rowMeta.id; String get rowId => rowMeta.id;
RowMetaPB get rowMeta => _rowMeta; RowMetaPB get rowMeta => _rowMeta;
CellMemCache get cellCache => _rowCache.cellCache;
List<CellContext> loadCells() => _rowCache.loadCells(rowMeta); List<CellContext> loadCells() => _rowCache.loadCells(rowMeta);
Future<void> initialize() async {
await _rowBackendSvc.initRow(rowMeta.id);
_rowListener.start(
onMetaChanged: (newRowMeta) {
if (_isDisposed) {
return;
}
_rowMeta = newRowMeta;
_rowCache.setRowMeta(newRowMeta);
_onRowMetaChanged?.call();
},
);
}
void addListener({ void addListener({
OnRowChanged? onRowChanged, OnRowChanged? onRowChanged,
VoidCallback? onMetaChanged, VoidCallback? onMetaChanged,

View File

@ -2,9 +2,7 @@ import 'dart:async';
import 'dart:collection'; import 'dart:collection';
import 'package:appflowy/plugins/database/application/row/row_service.dart'; import 'package:appflowy/plugins/database/application/row/row_service.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import '../defines.dart'; import '../defines.dart';
import '../field/field_controller.dart'; import '../field/field_controller.dart';
@ -93,17 +91,6 @@ class DatabaseViewCache {
(reorderRow) => _rowCache.reorderSingleRow(reorderRow), (reorderRow) => _rowCache.reorderSingleRow(reorderRow),
(err) => Log.error(err), (err) => Log.error(err),
), ),
onReloadRows: () {
final payload = DatabaseViewIdPB(value: viewId);
DatabaseEventGetAllRows(payload).send().then((result) {
result.fold(
(rows) {
_rowCache.setInitialRows(rows.items);
},
(err) => Log.error(err),
);
});
},
); );
_rowCache.onRowsChanged( _rowCache.onRowsChanged(

View File

@ -32,7 +32,6 @@ class DatabaseViewListener {
required ReorderAllRowsCallback onReorderAllRows, required ReorderAllRowsCallback onReorderAllRows,
required SingleRowCallback onReorderSingleRow, required SingleRowCallback onReorderSingleRow,
required RowsVisibilityCallback onRowsVisibilityChanged, required RowsVisibilityCallback onRowsVisibilityChanged,
required void Function() onReloadRows,
}) { }) {
// Stop any existing listener // Stop any existing listener
_listener?.stop(); _listener?.stop();
@ -47,7 +46,6 @@ class DatabaseViewListener {
onReorderAllRows, onReorderAllRows,
onReorderSingleRow, onReorderSingleRow,
onRowsVisibilityChanged, onRowsVisibilityChanged,
onReloadRows,
), ),
); );
} }
@ -59,7 +57,6 @@ class DatabaseViewListener {
ReorderAllRowsCallback onReorderAllRows, ReorderAllRowsCallback onReorderAllRows,
SingleRowCallback onReorderSingleRow, SingleRowCallback onReorderSingleRow,
RowsVisibilityCallback onRowsVisibilityChanged, RowsVisibilityCallback onRowsVisibilityChanged,
void Function() onReloadRows,
) { ) {
switch (ty) { switch (ty) {
case DatabaseNotification.DidUpdateViewRowsVisibility: case DatabaseNotification.DidUpdateViewRowsVisibility:
@ -94,9 +91,6 @@ class DatabaseViewListener {
(error) => onReorderSingleRow(FlowyResult.failure(error)), (error) => onReorderSingleRow(FlowyResult.failure(error)),
); );
break; break;
case DatabaseNotification.ReloadRows:
onReloadRows();
break;
default: default:
break; break;
} }

View File

@ -26,6 +26,7 @@ class RowBloc extends Bloc<RowEvent, RowState> {
_dispatch(); _dispatch();
_startListening(); _startListening();
_init(); _init();
rowController.initialize();
} }
final FieldController fieldController; final FieldController fieldController;

View File

@ -342,7 +342,7 @@ class _GridRowsState extends State<_GridRows> {
child: ReorderableListView.builder( child: ReorderableListView.builder(
/// This is a workaround related to /// This is a workaround related to
/// https://github.com/flutter/flutter/issues/25652 /// https://github.com/flutter/flutter/issues/25652
cacheExtent: 5000, cacheExtent: 600,
scrollController: widget.scrollController.verticalController, scrollController: widget.scrollController.verticalController,
physics: const ClampingScrollPhysics(), physics: const ClampingScrollPhysics(),
buildDefaultDragHandles: false, buildDefaultDragHandles: false,
@ -421,7 +421,7 @@ class _GridRowsState extends State<_GridRows> {
); );
final child = GridRow( final child = GridRow(
key: ValueKey(rowMeta.id), key: ValueKey(rowId),
fieldController: databaseController.fieldController, fieldController: databaseController.fieldController,
rowId: rowId, rowId: rowId,
viewId: viewId, viewId: viewId,

View File

@ -8,6 +8,7 @@ import 'package:appflowy/workspace/presentation/home/toast.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/size.dart'; import 'package:flowy_infra/size.dart';
import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart'; import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -202,9 +203,15 @@ class _URLAccessoryIconContainer extends StatelessWidget {
), ),
borderRadius: Corners.s6Border, borderRadius: Corners.s6Border,
), ),
child: FlowyHover(
style: HoverStyle(
backgroundColor: AFThemeExtension.of(context).background,
hoverColor: AFThemeExtension.of(context).lightGreyHover,
),
child: Center( child: Center(
child: child, child: child,
), ),
),
); );
} }
} }

View File

@ -964,7 +964,7 @@ dependencies = [
[[package]] [[package]]
name = "collab" name = "collab"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
@ -989,7 +989,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-database" name = "collab-database"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -1018,7 +1018,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-document" name = "collab-document"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
@ -1038,7 +1038,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-entity" name = "collab-entity"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -1057,7 +1057,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-folder" name = "collab-folder"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
@ -1100,7 +1100,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-plugins" name = "collab-plugins"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-stream", "async-stream",
@ -1180,7 +1180,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-user" name = "collab-user"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",

View File

@ -116,13 +116,13 @@ custom-protocol = ["tauri/custom-protocol"]
# 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 = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
# Working directory: frontend # Working directory: frontend
# To update the commit ID, run: # To update the commit ID, run:

View File

@ -947,7 +947,7 @@ dependencies = [
[[package]] [[package]]
name = "collab" name = "collab"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
@ -972,7 +972,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-database" name = "collab-database"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -1001,7 +1001,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-document" name = "collab-document"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
@ -1021,7 +1021,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-entity" name = "collab-entity"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -1040,7 +1040,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-folder" name = "collab-folder"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
@ -1083,7 +1083,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-plugins" name = "collab-plugins"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-stream", "async-stream",
@ -1163,7 +1163,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-user" name = "collab-user"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",

View File

@ -116,13 +116,13 @@ custom-protocol = ["tauri/custom-protocol"]
# 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 = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
# Working directory: frontend # Working directory: frontend
# To update the commit ID, run: # To update the commit ID, run:

View File

@ -825,7 +825,7 @@ dependencies = [
[[package]] [[package]]
name = "collab" name = "collab"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
@ -850,7 +850,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-database" name = "collab-database"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -879,7 +879,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-document" name = "collab-document"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
@ -899,7 +899,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-entity" name = "collab-entity"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -918,7 +918,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-folder" name = "collab-folder"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
@ -961,7 +961,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-plugins" name = "collab-plugins"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-stream", "async-stream",
@ -1041,7 +1041,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-user" name = "collab-user"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c714b6bd458420663c7a5b29370d6892902e995c#c714b6bd458420663c7a5b29370d6892902e995c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e51cde161cbce9af08f707ab1b3a3564eee90bac#e51cde161cbce9af08f707ab1b3a3564eee90bac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",

View File

@ -136,13 +136,13 @@ rocksdb = { git = "https://github.com/rust-rocksdb/rust-rocksdb", rev = "1710120
# 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 = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c714b6bd458420663c7a5b29370d6892902e995c" } collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
# Working directory: frontend # Working directory: frontend
# To update the commit ID, run: # To update the commit ID, run:

View File

@ -3,14 +3,13 @@ use std::convert::TryFrom;
use bytes::Bytes; use bytes::Bytes;
use collab_database::database::timestamp; use collab_database::database::timestamp;
use collab_database::entity::SelectOption;
use collab_database::fields::Field; use collab_database::fields::Field;
use collab_database::rows::{Row, RowId}; use collab_database::rows::{Row, RowId};
use flowy_database2::entities::*; use flowy_database2::entities::*;
use flowy_database2::event_map::DatabaseEvent; use flowy_database2::event_map::DatabaseEvent;
use flowy_database2::services::cell::CellBuilder; use flowy_database2::services::cell::CellBuilder;
use flowy_database2::services::field::{ use flowy_database2::services::field::{MultiSelectTypeOption, SingleSelectTypeOption};
MultiSelectTypeOption, SelectOption, SingleSelectTypeOption,
};
use flowy_database2::services::share::csv::CSVFormat; use flowy_database2::services::share::csv::CSVFormat;
use flowy_folder::entities::*; use flowy_folder::entities::*;
use flowy_folder::event_map::FolderEvent; use flowy_folder::event_map::FolderEvent;

View File

@ -1,5 +1,5 @@
use collab_database::database::{gen_database_id, gen_database_view_id, gen_row_id, DatabaseData}; use collab_database::database::{gen_database_id, gen_database_view_id, gen_row_id, DatabaseData};
use collab_database::entity::DatabaseView; use collab_database::entity::{DatabaseView, SelectOption, SelectOptionColor};
use collab_database::views::DatabaseLayout; use collab_database::views::DatabaseLayout;
use event_integration_test::database_event::TestRowBuilder; use event_integration_test::database_event::TestRowBuilder;
@ -9,8 +9,7 @@ use flowy_database2::entities::FieldType;
use flowy_database2::services::field::summary_type_option::summary::SummarizationTypeOption; use flowy_database2::services::field::summary_type_option::summary::SummarizationTypeOption;
use flowy_database2::services::field::translate_type_option::translate::TranslateTypeOption; use flowy_database2::services::field::translate_type_option::translate::TranslateTypeOption;
use flowy_database2::services::field::{ use flowy_database2::services::field::{
FieldBuilder, NumberFormat, NumberTypeOption, SelectOption, SelectOptionColor, FieldBuilder, NumberFormat, NumberTypeOption, SingleSelectTypeOption,
SingleSelectTypeOption,
}; };
use flowy_database2::services::field_settings::default_field_settings_for_fields; use flowy_database2::services::field_settings::default_field_settings_for_fields;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;

View File

@ -245,7 +245,7 @@ async fn get_row_event_test() {
assert!(row.is_some()); assert!(row.is_some());
let row = test.get_row_meta(&grid_view.id, &database.rows[0].id).await; let row = test.get_row_meta(&grid_view.id, &database.rows[0].id).await;
assert!(!row.document_id.is_empty()); assert!(row.document_id.is_some());
} }
#[tokio::test] #[tokio::test]

View File

@ -85,5 +85,6 @@ openssl_vendored = ["flowy-sqlite/openssl_vendored"]
# Enable/Disable AppFlowy Verbose Log Configuration # Enable/Disable AppFlowy Verbose Log Configuration
verbose_log = [ verbose_log = [
"flowy-document/verbose_log", "flowy-document/verbose_log",
"flowy-database2/verbose_log",
"client-api/sync_verbose_log" "client-api/sync_verbose_log"
] ]

View File

@ -61,3 +61,4 @@ flowy-codegen.workspace = true
[features] [features]
dart = ["flowy-codegen/dart", "flowy-notification/dart"] dart = ["flowy-codegen/dart", "flowy-notification/dart"]
ts = ["flowy-codegen/ts", "flowy-notification/tauri_ts"] ts = ["flowy-codegen/ts", "flowy-notification/tauri_ts"]
verbose_log = ["collab-database/verbose_log"]

View File

@ -55,8 +55,8 @@ pub struct RowMetaPB {
#[pb(index = 1)] #[pb(index = 1)]
pub id: String, pub id: String,
#[pb(index = 2)] #[pb(index = 2, one_of)]
pub document_id: String, pub document_id: Option<String>,
#[pb(index = 3, one_of)] #[pb(index = 3, one_of)]
pub icon: Option<String>, pub icon: Option<String>,
@ -64,8 +64,8 @@ pub struct RowMetaPB {
#[pb(index = 4, one_of)] #[pb(index = 4, one_of)]
pub cover: Option<String>, pub cover: Option<String>,
#[pb(index = 5)] #[pb(index = 5, one_of)]
pub is_document_empty: bool, pub is_document_empty: Option<bool>,
} }
#[derive(Debug, Default, ProtoBuf)] #[derive(Debug, Default, ProtoBuf)]
@ -74,25 +74,26 @@ pub struct RepeatedRowMetaPB {
pub items: Vec<RowMetaPB>, pub items: Vec<RowMetaPB>,
} }
impl std::convert::From<&RowDetail> for RowMetaPB { impl From<RowOrder> for RowMetaPB {
fn from(row_detail: &RowDetail) -> Self { fn from(data: RowOrder) -> Self {
Self { Self {
id: row_detail.row.id.to_string(), id: data.id.into_inner(),
document_id: row_detail.document_id.clone(), document_id: None,
icon: row_detail.meta.icon_url.clone(), icon: None,
cover: row_detail.meta.cover_url.clone(), cover: None,
is_document_empty: row_detail.meta.is_document_empty, is_document_empty: None,
} }
} }
} }
impl std::convert::From<RowDetail> for RowMetaPB { impl std::convert::From<RowDetail> for RowMetaPB {
fn from(row_detail: RowDetail) -> Self { fn from(row_detail: RowDetail) -> Self {
Self { Self {
id: row_detail.row.id.to_string(), id: row_detail.row.id.to_string(),
document_id: row_detail.document_id, document_id: Some(row_detail.document_id),
icon: row_detail.meta.icon_url, icon: row_detail.meta.icon_url,
cover: row_detail.meta.cover_url, cover: row_detail.meta.cover_url,
is_document_empty: row_detail.meta.is_document_empty, is_document_empty: Some(row_detail.meta.is_document_empty),
} }
} }
} }

View File

@ -1,3 +1,4 @@
use collab_database::entity::SelectOption;
use collab_database::rows::RowId; use collab_database::rows::RowId;
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
@ -5,7 +6,6 @@ use flowy_error::{ErrorCode, FlowyError};
use crate::entities::parser::NotEmptyStr; use crate::entities::parser::NotEmptyStr;
use crate::entities::SelectOptionPB; use crate::entities::SelectOptionPB;
use crate::services::field::SelectOption;
#[derive(Debug, Clone, Default, ProtoBuf)] #[derive(Debug, Clone, Default, ProtoBuf)]
pub struct ChecklistCellDataPB { pub struct ChecklistCellDataPB {

View File

@ -1,9 +1,8 @@
use crate::entities::parser::NotEmptyStr; use crate::entities::parser::NotEmptyStr;
use crate::entities::{CellIdPB, CellIdParams}; use crate::entities::{CellIdPB, CellIdParams};
use crate::services::field::checklist_type_option::ChecklistTypeOption; use crate::services::field::checklist_type_option::ChecklistTypeOption;
use crate::services::field::{ use crate::services::field::{MultiSelectTypeOption, SingleSelectTypeOption};
MultiSelectTypeOption, SelectOption, SelectOptionColor, SingleSelectTypeOption, use collab_database::entity::{SelectOption, SelectOptionColor};
};
use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::ErrorCode; use flowy_error::ErrorCode;

View File

@ -36,7 +36,7 @@ pub(crate) async fn get_database_data_handler(
.get_database_id_with_view_id(view_id.as_ref()) .get_database_id_with_view_id(view_id.as_ref())
.await?; .await?;
let database_editor = manager.get_database_editor(&database_id).await?; let database_editor = manager.get_database_editor(&database_id).await?;
let data = database_editor.get_database_data(view_id.as_ref()).await?; let data = database_editor.open_database(view_id.as_ref()).await?;
trace!( trace!(
"layout: {:?}, rows: {}, fields: {}", "layout: {:?}, rows: {}, fields: {}",
data.layout_type, data.layout_type,
@ -57,8 +57,14 @@ pub(crate) async fn get_all_rows_handler(
.get_database_id_with_view_id(view_id.as_ref()) .get_database_id_with_view_id(view_id.as_ref())
.await?; .await?;
let database_editor = manager.get_database_editor(&database_id).await?; let database_editor = manager.get_database_editor(&database_id).await?;
let data = database_editor.get_all_rows(view_id.as_ref()).await?; let row_details = database_editor
data_result_ok(data) .get_all_row_details(view_id.as_ref())
.await?;
let rows = row_details
.into_iter()
.map(|detail| RowMetaPB::from(detail.as_ref().clone()))
.collect::<Vec<RowMetaPB>>();
data_result_ok(RepeatedRowMetaPB { items: rows })
} }
#[tracing::instrument(level = "trace", skip_all, err)] #[tracing::instrument(level = "trace", skip_all, err)]
pub(crate) async fn open_database_handler( pub(crate) async fn open_database_handler(
@ -326,7 +332,7 @@ pub(crate) async fn switch_to_field_handler(
.await?; .await?;
let old_field = database_editor.get_field(&params.field_id).await; let old_field = database_editor.get_field(&params.field_id).await;
database_editor database_editor
.switch_to_field_type(&params.field_id, params.field_type) .switch_to_field_type(&params.view_id, &params.field_id, params.field_type)
.await?; .await?;
if let Some(new_type_option) = database_editor if let Some(new_type_option) = database_editor

View File

@ -307,6 +307,7 @@ impl DatabaseManager {
if should_remove { if should_remove {
trace!("remove database editor:{}", database_id); trace!("remove database editor:{}", database_id);
if let Some(editor) = editors.remove(&database_id) { if let Some(editor) = editors.remove(&database_id) {
editor.close_database().await;
self self
.removing_editor .removing_editor
.lock() .lock()
@ -793,8 +794,8 @@ impl DatabaseCollabService for WorkspaceDatabaseCollabServiceImpl {
Ok(collab) Ok(collab)
} }
fn persistence(&self) -> Option<Box<dyn DatabaseCollabPersistenceService>> { fn persistence(&self) -> Option<Arc<dyn DatabaseCollabPersistenceService>> {
Some(Box::new(DatabasePersistenceImpl { Some(Arc::new(DatabasePersistenceImpl {
user: self.user.clone(), user: self.user.clone(),
})) }))
} }

View File

@ -52,7 +52,6 @@ pub enum DatabaseNotification {
DidUpdateFieldSettings = 86, DidUpdateFieldSettings = 86,
// Trigger when Calculation changed // Trigger when Calculation changed
DidUpdateCalculation = 87, DidUpdateCalculation = 87,
ReloadRows = 88,
} }
impl std::convert::From<DatabaseNotification> for i32 { impl std::convert::From<DatabaseNotification> for i32 {

View File

@ -7,11 +7,11 @@ use crate::services::database::util::database_view_setting_pb_from_view;
use crate::services::database_view::{ use crate::services::database_view::{
DatabaseViewChanged, DatabaseViewOperation, DatabaseViews, EditorByViewId, DatabaseViewChanged, DatabaseViewOperation, DatabaseViews, EditorByViewId,
}; };
use crate::services::field::type_option_transform::transform_type_option;
use crate::services::field::{ use crate::services::field::{
default_type_option_data_from_type, select_type_option_from_field, transform_type_option, default_type_option_data_from_type, select_type_option_from_field, type_option_data_from_pb,
type_option_data_from_pb, ChecklistCellChangeset, RelationTypeOption, SelectOptionCellChangeset, ChecklistCellChangeset, RelationTypeOption, SelectOptionCellChangeset, StringCellData,
StringCellData, TimestampCellData, TimestampCellDataWrapper, TypeOptionCellDataHandler, TimestampCellData, TimestampCellDataWrapper, TypeOptionCellDataHandler, TypeOptionCellExt,
TypeOptionCellExt,
}; };
use crate::services::field_settings::{default_field_settings_by_layout_map, FieldSettings}; use crate::services::field_settings::{default_field_settings_by_layout_map, FieldSettings};
use crate::services::filter::{Filter, FilterChangeset}; use crate::services::filter::{Filter, FilterChangeset};
@ -36,9 +36,9 @@ use lib_infra::util::timestamp;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::{broadcast, RwLock}; use tokio::sync::{broadcast, RwLock};
use tracing::{debug, error, event, instrument, trace, warn}; use tokio_util::sync::CancellationToken;
use tracing::{debug, error, event, info, instrument, trace, warn};
#[derive(Clone)]
pub struct DatabaseEditor { pub struct DatabaseEditor {
pub(crate) database: Arc<RwLock<Database>>, pub(crate) database: Arc<RwLock<Database>>,
pub cell_cache: CellCache, pub cell_cache: CellCache,
@ -48,6 +48,7 @@ pub struct DatabaseEditor {
notification_sender: Arc<DebounceNotificationSender>, notification_sender: Arc<DebounceNotificationSender>,
user: Arc<dyn DatabaseUser>, user: Arc<dyn DatabaseUser>,
collab_builder: Arc<AppFlowyCollabBuilder>, collab_builder: Arc<AppFlowyCollabBuilder>,
database_cancellation: Arc<RwLock<Option<CancellationToken>>>,
} }
impl DatabaseEditor { impl DatabaseEditor {
@ -60,6 +61,7 @@ impl DatabaseEditor {
let notification_sender = Arc::new(DebounceNotificationSender::new(200)); let notification_sender = Arc::new(DebounceNotificationSender::new(200));
let cell_cache = AnyTypeCache::<u64>::new(); let cell_cache = AnyTypeCache::<u64>::new();
let database_id = database.read().await.get_database_id(); let database_id = database.read().await.get_database_id();
let database_cancellation = Arc::new(RwLock::new(None));
// Receive database sync state and send to frontend via the notification // Receive database sync state and send to frontend via the notification
observe_sync_state(&database_id, &database).await; observe_sync_state(&database_id, &database).await;
// observe_view_change(&database_id, &database).await; // observe_view_change(&database_id, &database).await;
@ -73,6 +75,7 @@ impl DatabaseEditor {
task_scheduler: task_scheduler.clone(), task_scheduler: task_scheduler.clone(),
cell_cache: cell_cache.clone(), cell_cache: cell_cache.clone(),
editor_by_view_id: editor_by_view_id.clone(), editor_by_view_id: editor_by_view_id.clone(),
database_cancellation: database_cancellation.clone(),
}); });
let database_views = Arc::new( let database_views = Arc::new(
@ -104,6 +107,7 @@ impl DatabaseEditor {
database_views, database_views,
notification_sender, notification_sender,
collab_builder, collab_builder,
database_cancellation,
}); });
observe_block_event(&database_id, &this).await; observe_block_event(&database_id, &this).await;
Ok(this) Ok(this)
@ -419,6 +423,7 @@ impl DatabaseEditor {
pub async fn switch_to_field_type( pub async fn switch_to_field_type(
&self, &self,
view_id: &str,
field_id: &str, field_id: &str,
new_field_type: FieldType, new_field_type: FieldType,
) -> FlowyResult<()> { ) -> FlowyResult<()> {
@ -441,11 +446,16 @@ impl DatabaseEditor {
.unwrap_or_else(|| default_type_option_data_from_type(new_field_type)); .unwrap_or_else(|| default_type_option_data_from_type(new_field_type));
let transformed_type_option = transform_type_option( let transformed_type_option = transform_type_option(
view_id,
field_id,
old_field_type, old_field_type,
new_field_type, new_field_type,
old_type_option_data, old_type_option_data,
new_type_option_data, new_type_option_data,
); &database,
)
.await;
database.update_field(field_id, |update| { database.update_field(field_id, |update| {
update update
.set_field_type(new_field_type.into()) .set_field_type(new_field_type.into())
@ -667,9 +677,9 @@ impl DatabaseEditor {
Ok(()) Ok(())
} }
pub async fn get_row_details(&self, view_id: &str) -> FlowyResult<Vec<Arc<RowDetail>>> { pub async fn get_all_row_details(&self, view_id: &str) -> FlowyResult<Vec<Arc<RowDetail>>> {
let view_editor = self.database_views.get_view_editor(view_id).await?; let view_editor = self.database_views.get_view_editor(view_id).await?;
Ok(view_editor.v_get_row_details().await) Ok(view_editor.v_get_all_row_details().await)
} }
pub async fn get_row(&self, view_id: &str, row_id: &RowId) -> Option<Row> { pub async fn get_row(&self, view_id: &str, row_id: &RowId) -> Option<Row> {
@ -731,10 +741,10 @@ impl DatabaseEditor {
let row_document_id = database.get_row_document_id(row_id)?; let row_document_id = database.get_row_document_id(row_id)?;
Some(RowMetaPB { Some(RowMetaPB {
id: row_id.clone().into_inner(), id: row_id.clone().into_inner(),
document_id: row_document_id, document_id: Some(row_document_id),
icon: row_meta.icon_url, icon: row_meta.icon_url,
cover: row_meta.cover_url, cover: row_meta.cover_url,
is_document_empty: row_meta.is_document_empty, is_document_empty: Some(row_meta.is_document_empty),
}) })
} else { } else {
warn!("the row:{} is exist in view:{}", row_id.as_str(), view_id); warn!("the row:{} is exist in view:{}", row_id.as_str(), view_id);
@ -786,13 +796,8 @@ impl DatabaseEditor {
// Notifies the client that the row meta has been updated. // Notifies the client that the row meta has been updated.
send_notification(row_id.as_str(), DatabaseNotification::DidUpdateRowMeta) send_notification(row_id.as_str(), DatabaseNotification::DidUpdateRowMeta)
.payload(RowMetaPB::from(&row_detail)) .payload(RowMetaPB::from(row_detail))
.send(); .send();
// Update the last modified time of the row
self
.update_last_modified_time(row_detail.clone(), &changeset.view_id)
.await;
} }
} }
@ -885,24 +890,6 @@ impl DatabaseEditor {
self.update_cell(view_id, row_id, field_id, new_cell).await self.update_cell(view_id, row_id, field_id, new_cell).await
} }
async fn update_last_modified_time(&self, row_detail: RowDetail, view_id: &str) {
self
.database
.write()
.await
.update_row(row_detail.row.id.clone(), |row_update| {
row_update.set_last_modified(timestamp());
})
.await;
let editor = self.database_views.get_view_editor(view_id).await;
if let Ok(editor) = editor {
editor
.v_did_update_row(&Some(row_detail.clone()), &row_detail, None)
.await;
}
}
/// Update a cell in the database. /// Update a cell in the database.
/// This will notify all views that the cell has been updated. /// This will notify all views that the cell has been updated.
pub async fn update_cell( pub async fn update_cell(
@ -919,7 +906,9 @@ impl DatabaseEditor {
.write() .write()
.await .await
.update_row(row_id.clone(), |row_update| { .update_row(row_id.clone(), |row_update| {
row_update.update_cells(|cell_update| { row_update
.set_last_modified(timestamp())
.update_cells(|cell_update| {
cell_update.insert(field_id, new_cell); cell_update.insert(field_id, new_cell);
}); });
}) })
@ -1170,7 +1159,7 @@ impl DatabaseEditor {
let to_row = if to_row.is_some() { let to_row = if to_row.is_some() {
to_row to_row
} else { } else {
let row_details = self.get_row_details(view_id).await?; let row_details = self.get_all_row_details(view_id).await?;
row_details row_details
.last() .last()
.map(|row_detail| row_detail.row.id.clone()) .map(|row_detail| row_detail.row.id.clone())
@ -1189,7 +1178,9 @@ impl DatabaseEditor {
.write() .write()
.await .await
.update_row(row_detail.row.id, |row| { .update_row(row_detail.row.id, |row| {
row.set_cells(Cells::from(row_changeset.cell_by_field_id.clone())); row
.set_last_modified(timestamp())
.set_cells(Cells::from(row_changeset.cell_by_field_id.clone()));
}) })
.await; .await;
}, },
@ -1296,14 +1287,34 @@ impl DatabaseEditor {
Ok(database_view_setting_pb_from_view(view)) Ok(database_view_setting_pb_from_view(view))
} }
pub async fn get_database_data(&self, view_id: &str) -> FlowyResult<DatabasePB> { pub async fn close_database(&self) {
let database_view = self.database_views.get_view_editor(view_id).await?; let cancellation = self.database_cancellation.read().await;
let view = database_view if let Some(cancellation) = &*cancellation {
.v_get_view() info!("Cancel database operation");
.await cancellation.cancel();
.ok_or_else(FlowyError::record_not_found)?; }
}
pub async fn open_database(&self, view_id: &str) -> FlowyResult<DatabasePB> {
let view_layout = self.database.read().await.get_database_view_layout(view_id);
let new_token = CancellationToken::new();
if let Some(old_token) = self
.database_cancellation
.write()
.await
.replace(new_token.clone())
{
old_token.cancel();
}
let row_details = self
.database_views
.get_view_editor(view_id)
.await?
.v_get_all_row_details()
.await;
let row_details = database_view.v_get_row_details().await;
let (database_id, fields, is_linked) = { let (database_id, fields, is_linked) = {
let database = self.database.read().await; let database = self.database.read().await;
let database_id = database.get_database_id(); let database_id = database.get_database_id();
@ -1318,7 +1329,7 @@ impl DatabaseEditor {
let rows = row_details let rows = row_details
.into_iter() .into_iter()
.map(|detail| RowMetaPB::from(detail.as_ref())) .map(|order| RowMetaPB::from(order.as_ref().clone()))
.collect::<Vec<RowMetaPB>>(); .collect::<Vec<RowMetaPB>>();
trace!( trace!(
@ -1327,25 +1338,16 @@ impl DatabaseEditor {
fields.len(), fields.len(),
rows.len() rows.len()
); );
self.database_cancellation.write().await.take();
Ok(DatabasePB { Ok(DatabasePB {
id: database_id, id: database_id,
fields, fields,
rows, rows,
layout_type: view.layout.into(), layout_type: view_layout.into(),
is_linked, is_linked,
}) })
} }
pub async fn get_all_rows(&self, view_id: &str) -> FlowyResult<RepeatedRowMetaPB> {
let database_view = self.database_views.get_view_editor(view_id).await?;
let row_details = database_view.v_get_row_details().await;
let rows = row_details
.into_iter()
.map(|detail| RowMetaPB::from(detail.as_ref()))
.collect::<Vec<RowMetaPB>>();
Ok(RepeatedRowMetaPB { items: rows })
}
pub async fn export_csv(&self, style: CSVFormat) -> FlowyResult<String> { pub async fn export_csv(&self, style: CSVFormat) -> FlowyResult<String> {
let database = self.database.clone(); let database = self.database.clone();
let database_guard = database.read().await; let database_guard = database.read().await;
@ -1467,6 +1469,7 @@ struct DatabaseViewOperationImpl {
task_scheduler: Arc<RwLock<TaskDispatcher>>, task_scheduler: Arc<RwLock<TaskDispatcher>>,
cell_cache: CellCache, cell_cache: CellCache,
editor_by_view_id: Arc<RwLock<EditorByViewId>>, editor_by_view_id: Arc<RwLock<EditorByViewId>>,
database_cancellation: Arc<RwLock<Option<CancellationToken>>>,
} }
#[async_trait] #[async_trait]
@ -1559,19 +1562,30 @@ impl DatabaseViewOperation for DatabaseViewOperationImpl {
} }
} }
async fn get_row_details(&self, view_id: &str) -> Vec<Arc<RowDetail>> { async fn get_all_row_details(&self, view_id: &str) -> Vec<Arc<RowDetail>> {
let view_id = view_id.to_string(); let view_id = view_id.to_string();
let row_orders = self.database.read().await.get_row_orders_for_view(&view_id); let row_orders = self.database.read().await.get_row_orders_for_view(&view_id);
trace!("total row orders: {}", row_orders.len()); trace!("{} has total row orders: {}", view_id, row_orders.len());
let mut row_details_list = vec![]; let mut row_details_list = vec![];
// Loading the rows in chunks of 10 rows in order to prevent blocking the main asynchronous runtime // Loading the rows in chunks of 10 rows in order to prevent blocking the main asynchronous runtime
const CHUNK_SIZE: usize = 10; const CHUNK_SIZE: usize = 10;
let cancellation = self
.database_cancellation
.read()
.await
.as_ref()
.map(|c| c.clone());
for chunk in row_orders.chunks(CHUNK_SIZE) { for chunk in row_orders.chunks(CHUNK_SIZE) {
let database_read_guard = self.database.read().await; let database_read_guard = self.database.read().await;
let chunk = chunk.to_vec(); let chunk = chunk.to_vec();
let rows = database_read_guard.get_rows_from_row_orders(&chunk).await; let rows = database_read_guard.get_rows_from_row_orders(&chunk).await;
for row in rows { for row in rows {
if let Some(cancellation) = &cancellation {
if cancellation.is_cancelled() {
info!("Get all database row is cancelled:{}", view_id);
return vec![];
}
}
match database_read_guard.get_row_detail(&row.id).await { match database_read_guard.get_row_detail(&row.id).await {
None => warn!("Failed to get row detail for row: {}", row.id.as_str()), None => warn!("Failed to get row detail for row: {}", row.id.as_str()),
Some(row_details) => { Some(row_details) => {

View File

@ -10,9 +10,7 @@ use flowy_notification::{DebounceNotificationSender, NotificationBuilder};
use futures::StreamExt; use futures::StreamExt;
use lib_dispatch::prelude::af_spawn; use lib_dispatch::prelude::af_spawn;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use tokio_util::sync::CancellationToken;
use tracing::{trace, warn}; use tracing::{trace, warn};
pub(crate) async fn observe_sync_state(database_id: &str, database: &Arc<RwLock<Database>>) { pub(crate) async fn observe_sync_state(database_id: &str, database: &Arc<RwLock<Database>>) {
@ -147,7 +145,7 @@ pub(crate) async fn observe_block_event(database_id: &str, database_editor: &Arc
.subscribe_block_event(); .subscribe_block_event();
let database_editor = Arc::downgrade(database_editor); let database_editor = Arc::downgrade(database_editor);
af_spawn(async move { af_spawn(async move {
let token = CancellationToken::new(); // let token = CancellationToken::new();
while let Ok(event) = block_event_rx.recv().await { while let Ok(event) = block_event_rx.recv().await {
if database_editor.upgrade().is_none() { if database_editor.upgrade().is_none() {
break; break;
@ -170,23 +168,17 @@ pub(crate) async fn observe_block_event(database_id: &str, database_editor: &Arc
.send(); .send();
} }
let cloned_token = token.clone(); // let cloned_token = token.clone();
let cloned_database_editor = database_editor.clone(); // tokio::spawn(async move {
tokio::spawn(async move { // tokio::time::sleep(Duration::from_secs(2)).await;
tokio::time::sleep(Duration::from_secs(2)).await; // if cloned_token.is_cancelled() {
if cloned_token.is_cancelled() { // }
return; // // if let Some(database_editor) = cloned_database_editor.upgrade() {
} // // TODO(nathan): calculate inserted row with RowsVisibilityChangePB
if let Some(database_editor) = cloned_database_editor.upgrade() { // // for view_editor in database_editor.database_views.editors().await {
for view_editor in database_editor.database_views.editors().await { // // }
send_notification( // // }
&view_editor.view_id.clone(), // });
DatabaseNotification::ReloadRows,
)
.send();
}
}
});
}, },
} }
} }

View File

@ -313,8 +313,8 @@ impl DatabaseViewEditor {
} }
#[instrument(level = "info", skip(self))] #[instrument(level = "info", skip(self))]
pub async fn v_get_row_details(&self) -> Vec<Arc<RowDetail>> { pub async fn v_get_all_row_details(&self) -> Vec<Arc<RowDetail>> {
let mut rows = self.delegate.get_row_details(&self.view_id).await; let mut rows = self.delegate.get_all_row_details(&self.view_id).await;
self.v_filter_rows(&mut rows).await; self.v_filter_rows(&mut rows).await;
self.v_sort_rows(&mut rows).await; self.v_sort_rows(&mut rows).await;
rows rows
@ -937,7 +937,7 @@ impl DatabaseViewEditor {
let (_, row_detail) = self.delegate.get_row_detail(&self.view_id, &row_id).await?; let (_, row_detail) = self.delegate.get_row_detail(&self.view_id, &row_id).await?;
Some(CalendarEventPB { Some(CalendarEventPB {
row_meta: RowMetaPB::from(row_detail.as_ref()), row_meta: RowMetaPB::from(row_detail.as_ref().clone()),
date_field_id: date_field.id.clone(), date_field_id: date_field.id.clone(),
title, title,
timestamp, timestamp,
@ -1000,7 +1000,7 @@ impl DatabaseViewEditor {
let (_, row_detail) = self.delegate.get_row_detail(&self.view_id, &row_id).await?; let (_, row_detail) = self.delegate.get_row_detail(&self.view_id, &row_id).await?;
let event = CalendarEventPB { let event = CalendarEventPB {
row_meta: RowMetaPB::from(row_detail.as_ref()), row_meta: RowMetaPB::from(row_detail.as_ref().clone()),
date_field_id: calendar_setting.field_id.clone(), date_field_id: calendar_setting.field_id.clone(),
title, title,
timestamp, timestamp,

View File

@ -53,7 +53,7 @@ impl FilterDelegate for DatabaseViewFilterDelegateImpl {
} }
async fn get_rows(&self, view_id: &str) -> Vec<Arc<RowDetail>> { async fn get_rows(&self, view_id: &str) -> Vec<Arc<RowDetail>> {
self.0.get_row_details(view_id).await self.0.get_all_row_details(view_id).await
} }
async fn get_row(&self, view_id: &str, rows_id: &RowId) -> Option<(usize, Arc<RowDetail>)> { async fn get_row(&self, view_id: &str, rows_id: &RowId) -> Option<(usize, Arc<RowDetail>)> {

View File

@ -97,7 +97,7 @@ impl GroupControllerDelegate for GroupControllerDelegateImpl {
} }
async fn get_all_rows(&self, view_id: &str) -> Vec<Arc<RowDetail>> { async fn get_all_rows(&self, view_id: &str) -> Vec<Arc<RowDetail>> {
let mut row_details = self.delegate.get_row_details(view_id).await; let mut row_details = self.delegate.get_all_row_details(view_id).await;
self.filter_controller.filter_rows(&mut row_details).await; self.filter_controller.filter_rows(&mut row_details).await;
row_details row_details
} }

View File

@ -56,7 +56,7 @@ pub trait DatabaseViewOperation: Send + Sync + 'static {
async fn get_row_detail(&self, view_id: &str, row_id: &RowId) -> Option<(usize, Arc<RowDetail>)>; async fn get_row_detail(&self, view_id: &str, row_id: &RowId) -> Option<(usize, Arc<RowDetail>)>;
/// Returns all the rows in the view /// Returns all the rows in the view
async fn get_row_details(&self, view_id: &str) -> Vec<Arc<RowDetail>>; async fn get_all_row_details(&self, view_id: &str) -> Vec<Arc<RowDetail>>;
async fn remove_row(&self, row_id: &RowId) -> Option<Row>; async fn remove_row(&self, row_id: &RowId) -> Option<Row>;

View File

@ -61,7 +61,7 @@ impl SortDelegate for DatabaseViewSortDelegateImpl {
async fn get_rows(&self, view_id: &str) -> Vec<Arc<RowDetail>> { async fn get_rows(&self, view_id: &str) -> Vec<Arc<RowDetail>> {
let view_id = view_id.to_string(); let view_id = view_id.to_string();
let mut row_details = self.delegate.get_row_details(&view_id).await; let mut row_details = self.delegate.get_all_row_details(&view_id).await;
self.filter_controller.filter_rows(&mut row_details).await; self.filter_controller.filter_rows(&mut row_details).await;
row_details row_details
} }

View File

@ -1,5 +1,6 @@
mod field_builder; mod field_builder;
mod field_operation; mod field_operation;
pub(crate) mod type_option_transform;
pub mod type_options; pub mod type_options;
pub use field_builder::*; pub use field_builder::*;

View File

@ -0,0 +1,127 @@
use crate::entities::FieldType;
use crate::services::field::summary_type_option::summary::SummarizationTypeOption;
use crate::services::field::translate_type_option::translate::TranslateTypeOption;
use crate::services::field::{
CheckboxTypeOption, ChecklistTypeOption, DateTypeOption, MultiSelectTypeOption, NumberTypeOption,
RelationTypeOption, RichTextTypeOption, SingleSelectTypeOption, TimeTypeOption,
TimestampTypeOption, TypeOptionTransform, URLTypeOption,
};
use async_trait::async_trait;
use collab_database::database::Database;
use collab_database::fields::TypeOptionData;
pub async fn transform_type_option(
view_id: &str,
field_id: &str,
old_field_type: FieldType,
new_field_type: FieldType,
old_type_option_data: Option<TypeOptionData>,
new_type_option_data: TypeOptionData,
database: &Database,
) -> TypeOptionData {
if let Some(old_type_option_data) = old_type_option_data {
let mut transform_handler =
get_type_option_transform_handler(new_type_option_data, new_field_type);
transform_handler
.transform(
view_id,
field_id,
old_field_type,
old_type_option_data,
database,
)
.await;
transform_handler.to_type_option_data()
} else {
new_type_option_data
}
}
/// A helper trait that used to erase the `Self` of `TypeOption` trait to make it become a Object-safe trait.
#[async_trait]
pub trait TypeOptionTransformHandler: Send + Sync {
async fn transform(
&mut self,
view_id: &str,
field_id: &str,
old_type_option_field_type: FieldType,
old_type_option_data: TypeOptionData,
database: &Database,
);
fn to_type_option_data(&self) -> TypeOptionData;
}
#[async_trait]
impl<T> TypeOptionTransformHandler for T
where
T: TypeOptionTransform + Clone,
{
async fn transform(
&mut self,
view_id: &str,
field_id: &str,
old_type_option_field_type: FieldType,
old_type_option_data: TypeOptionData,
database: &Database,
) {
self
.transform_type_option(
view_id,
field_id,
old_type_option_field_type,
old_type_option_data,
database,
)
.await
}
fn to_type_option_data(&self) -> TypeOptionData {
self.clone().into()
}
}
fn get_type_option_transform_handler(
type_option_data: TypeOptionData,
field_type: FieldType,
) -> Box<dyn TypeOptionTransformHandler> {
match field_type {
FieldType::RichText => {
Box::new(RichTextTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::Number => {
Box::new(NumberTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::DateTime => {
Box::new(DateTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::LastEditedTime | FieldType::CreatedTime => {
Box::new(TimestampTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::SingleSelect => Box::new(SingleSelectTypeOption::from(type_option_data))
as Box<dyn TypeOptionTransformHandler>,
FieldType::MultiSelect => {
Box::new(MultiSelectTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::Checkbox => {
Box::new(CheckboxTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::URL => {
Box::new(URLTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::Checklist => {
Box::new(ChecklistTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::Relation => {
Box::new(RelationTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::Summary => Box::new(SummarizationTypeOption::from(type_option_data))
as Box<dyn TypeOptionTransformHandler>,
FieldType::Time => {
Box::new(TimeTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::Translate => {
Box::new(TranslateTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
}
}

View File

@ -1,15 +1,15 @@
use std::cmp::Ordering; use collab_database::entity::SelectOption;
use collab_database::fields::{TypeOptionData, TypeOptionDataBuilder}; use collab_database::fields::{TypeOptionData, TypeOptionDataBuilder};
use collab_database::rows::Cell; use collab_database::rows::Cell;
use flowy_error::FlowyResult; use flowy_error::FlowyResult;
use std::cmp::Ordering;
use crate::entities::{ChecklistCellDataPB, ChecklistFilterPB, SelectOptionPB}; use crate::entities::{ChecklistCellDataPB, ChecklistFilterPB, SelectOptionPB};
use crate::services::cell::{CellDataChangeset, CellDataDecoder}; use crate::services::cell::{CellDataChangeset, CellDataDecoder};
use crate::services::field::checklist_type_option::{ChecklistCellChangeset, ChecklistCellData}; use crate::services::field::checklist_type_option::{ChecklistCellChangeset, ChecklistCellData};
use crate::services::field::{ use crate::services::field::{
SelectOption, TypeOption, TypeOptionCellData, TypeOptionCellDataCompare, TypeOption, TypeOptionCellData, TypeOptionCellDataCompare, TypeOptionCellDataFilter,
TypeOptionCellDataFilter, TypeOptionCellDataSerde, TypeOptionTransform, SELECTION_IDS_SEPARATOR, TypeOptionCellDataSerde, TypeOptionTransform, SELECTION_IDS_SEPARATOR,
}; };
use crate::services::sort::SortCondition; use crate::services::sort::SortCondition;

View File

@ -1,6 +1,7 @@
use crate::entities::FieldType; use crate::entities::FieldType;
use crate::services::field::{SelectOption, TypeOptionCellData, CELL_DATA}; use crate::services::field::{TypeOptionCellData, CELL_DATA};
use collab::util::AnyMapExt; use collab::util::AnyMapExt;
use collab_database::entity::SelectOption;
use collab_database::rows::{new_cell_builder, Cell}; use collab_database::rows::{new_cell_builder, Cell};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Debug; use std::fmt::Debug;

View File

@ -1,8 +1,8 @@
use collab_database::entity::SelectOption;
use collab_database::fields::Field; use collab_database::fields::Field;
use collab_database::rows::Cell; use collab_database::rows::Cell;
use crate::entities::{ChecklistFilterConditionPB, ChecklistFilterPB}; use crate::entities::{ChecklistFilterConditionPB, ChecklistFilterPB};
use crate::services::field::SelectOption;
use crate::services::filter::PreFillCellsWithFilter; use crate::services::filter::PreFillCellsWithFilter;
impl ChecklistFilterPB { impl ChecklistFilterPB {

View File

@ -1,18 +1,17 @@
use collab::util::AnyMapExt; use collab::util::AnyMapExt;
use std::cmp::Ordering; use collab_database::entity::SelectOption;
use collab_database::fields::{TypeOptionData, TypeOptionDataBuilder}; use collab_database::fields::{TypeOptionData, TypeOptionDataBuilder};
use collab_database::rows::Cell; use collab_database::rows::Cell;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use flowy_error::FlowyResult; use flowy_error::FlowyResult;
use crate::entities::{FieldType, SelectOptionCellDataPB, SelectOptionFilterPB}; use crate::entities::{FieldType, SelectOptionCellDataPB, SelectOptionFilterPB};
use crate::services::cell::CellDataChangeset; use crate::services::cell::CellDataChangeset;
use crate::services::field::{ use crate::services::field::{
default_order, SelectOption, SelectOptionCellChangeset, SelectOptionIds, default_order, SelectOptionCellChangeset, SelectOptionIds, SelectTypeOptionSharedAction,
SelectTypeOptionSharedAction, TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, TypeOptionCellDataSerde,
TypeOptionCellDataSerde,
}; };
use crate::services::sort::SortCondition; use crate::services::sort::SortCondition;
@ -176,39 +175,10 @@ impl TypeOptionCellDataCompare for MultiSelectTypeOption {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::entities::FieldType;
use crate::services::cell::CellDataChangeset; use crate::services::cell::CellDataChangeset;
use crate::services::field::type_options::selection_type_option::*; use crate::services::field::type_options::selection_type_option::*;
use crate::services::field::MultiSelectTypeOption; use crate::services::field::MultiSelectTypeOption;
use crate::services::field::{CheckboxTypeOption, TypeOptionTransform}; use collab_database::entity::SelectOption;
#[test]
fn multi_select_transform_with_checkbox_type_option_test() {
let checkbox_type_option = CheckboxTypeOption();
let mut multi_select = MultiSelectTypeOption::default();
multi_select.transform_type_option(FieldType::Checkbox, checkbox_type_option.clone().into());
debug_assert_eq!(multi_select.options.len(), 2);
// Already contain the yes/no option. It doesn't need to insert new options
multi_select.transform_type_option(FieldType::Checkbox, checkbox_type_option.into());
debug_assert_eq!(multi_select.options.len(), 2);
}
#[test]
fn multi_select_transform_with_single_select_type_option_test() {
let google = SelectOption::new("Google");
let facebook = SelectOption::new("Facebook");
let single_select = SingleSelectTypeOption {
options: vec![google, facebook],
disable_color: false,
};
let mut multi_select = MultiSelectTypeOption {
options: vec![],
disable_color: false,
};
multi_select.transform_type_option(FieldType::MultiSelect, single_select.into());
debug_assert_eq!(multi_select.options.len(), 2);
}
#[test] #[test]
fn multi_select_insert_multi_option_test() { fn multi_select_insert_multi_option_test() {

View File

@ -1,9 +1,10 @@
use collab_database::entity::SelectOption;
use collab_database::fields::Field; use collab_database::fields::Field;
use collab_database::rows::Cell; use collab_database::rows::Cell;
use crate::entities::{SelectOptionFilterConditionPB, SelectOptionFilterPB}; use crate::entities::{SelectOptionFilterConditionPB, SelectOptionFilterPB};
use crate::services::cell::insert_select_option_cell; use crate::services::cell::insert_select_option_cell;
use crate::services::field::{select_type_option_from_field, SelectOption}; use crate::services::field::select_type_option_from_field;
use crate::services::filter::PreFillCellsWithFilter; use crate::services::filter::PreFillCellsWithFilter;
impl SelectOptionFilterPB { impl SelectOptionFilterPB {
@ -132,7 +133,7 @@ impl PreFillCellsWithFilter for SelectOptionFilterPB {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::entities::{SelectOptionFilterConditionPB, SelectOptionFilterPB}; use crate::entities::{SelectOptionFilterConditionPB, SelectOptionFilterPB};
use crate::services::field::SelectOption; use collab_database::entity::SelectOption;
#[test] #[test]
fn select_option_filter_is_empty_test() { fn select_option_filter_is_empty_test() {

View File

@ -1,50 +1,6 @@
use crate::entities::SelectOptionCellDataPB; use crate::entities::SelectOptionCellDataPB;
use crate::services::field::SelectOptionIds; use crate::services::field::SelectOptionIds;
use collab_database::database::gen_option_id; use collab_database::entity::SelectOption;
use serde::{Deserialize, Serialize};
/// [SelectOption] represents an option for a single select, and multiple select.
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct SelectOption {
pub id: String,
pub name: String,
pub color: SelectOptionColor,
}
impl SelectOption {
pub fn new(name: &str) -> Self {
SelectOption {
id: gen_option_id(),
name: name.to_owned(),
color: SelectOptionColor::default(),
}
}
pub fn with_color(name: &str, color: SelectOptionColor) -> Self {
SelectOption {
id: gen_option_id(),
name: name.to_owned(),
color,
}
}
}
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
#[repr(u8)]
#[derive(Default)]
pub enum SelectOptionColor {
#[default]
Purple = 0,
Pink = 1,
LightPink = 2,
Orange = 3,
Yellow = 4,
Lime = 5,
Green = 6,
Aqua = 7,
Blue = 8,
}
#[derive(Debug)] #[derive(Debug)]
pub struct SelectOptionCellData { pub struct SelectOptionCellData {
pub select_options: Vec<SelectOption>, pub select_options: Vec<SelectOption>,

View File

@ -1,18 +1,19 @@
use std::str::FromStr; use async_trait::async_trait;
use bytes::Bytes; use bytes::Bytes;
use collab_database::database::Database;
use collab_database::entity::{SelectOption, SelectOptionColor};
use collab_database::fields::{Field, TypeOptionData}; use collab_database::fields::{Field, TypeOptionData};
use collab_database::rows::Cell; use collab_database::rows::Cell;
use flowy_error::{internal_error, ErrorCode, FlowyResult}; use flowy_error::{internal_error, ErrorCode, FlowyResult};
use std::str::FromStr;
use crate::entities::{CheckboxCellDataPB, FieldType, SelectOptionCellDataPB}; use crate::entities::{CheckboxCellDataPB, FieldType, SelectOptionCellDataPB};
use crate::services::cell::{CellDataDecoder, CellProtobufBlobParser}; use crate::services::cell::{CellDataDecoder, CellProtobufBlobParser};
use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformHelper; use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformHelper;
use crate::services::field::{ use crate::services::field::{
make_selected_options, MultiSelectTypeOption, SelectOption, SelectOptionCellData, make_selected_options, MultiSelectTypeOption, SelectOptionCellData, SelectOptionIds,
SelectOptionColor, SelectOptionIds, SingleSelectTypeOption, TypeOption, TypeOptionCellDataSerde, SingleSelectTypeOption, StringCellData, TypeOption, TypeOptionCellDataSerde, TypeOptionTransform,
TypeOptionTransform, SELECTION_IDS_SEPARATOR, SELECTION_IDS_SEPARATOR,
}; };
/// Defines the shared actions used by SingleSelect or Multi-Select. /// Defines the shared actions used by SingleSelect or Multi-Select.
@ -68,20 +69,28 @@ pub trait SelectTypeOptionSharedAction: Send + Sync {
fn mut_options(&mut self) -> &mut Vec<SelectOption>; fn mut_options(&mut self) -> &mut Vec<SelectOption>;
} }
#[async_trait]
impl<T> TypeOptionTransform for T impl<T> TypeOptionTransform for T
where where
T: SelectTypeOptionSharedAction + TypeOption<CellData = SelectOptionIds> + CellDataDecoder, T: SelectTypeOptionSharedAction + TypeOption<CellData = SelectOptionIds> + CellDataDecoder,
{ {
fn transform_type_option( async fn transform_type_option(
&mut self, &mut self,
_old_type_option_field_type: FieldType, view_id: &str,
_old_type_option_data: TypeOptionData, field_id: &str,
old_type_option_field_type: FieldType,
old_type_option_data: TypeOptionData,
database: &Database,
) { ) {
SelectOptionTypeOptionTransformHelper::transform_type_option( SelectOptionTypeOptionTransformHelper::transform_type_option(
self, self,
&_old_type_option_field_type, view_id,
_old_type_option_data, field_id,
); &old_type_option_field_type,
old_type_option_data,
database,
)
.await;
} }
} }
@ -101,6 +110,15 @@ where
_field: &Field, _field: &Field,
) -> Option<<Self as TypeOption>::CellData> { ) -> Option<<Self as TypeOption>::CellData> {
match from_field_type { match from_field_type {
FieldType::RichText => {
let text_cell = StringCellData::from(cell).into_inner();
let mut transformed_ids = Vec::new();
let options = self.options();
if let Some(option) = options.iter().find(|option| option.name == text_cell) {
transformed_ids.push(option.id.clone());
}
Some(SelectOptionIds::from(transformed_ids))
},
FieldType::Checkbox => { FieldType::Checkbox => {
let cell_content = CheckboxCellDataPB::from(cell).to_string(); let cell_content = CheckboxCellDataPB::from(cell).to_string();
let mut transformed_ids = Vec::new(); let mut transformed_ids = Vec::new();
@ -110,7 +128,6 @@ where
} }
Some(SelectOptionIds::from(transformed_ids)) Some(SelectOptionIds::from(transformed_ids))
}, },
FieldType::RichText => Some(SelectOptionIds::from(cell)),
FieldType::SingleSelect | FieldType::MultiSelect => Some(SelectOptionIds::from(cell)), FieldType::SingleSelect | FieldType::MultiSelect => Some(SelectOptionIds::from(cell)),
_ => None, _ => None,
} }

View File

@ -1,7 +1,7 @@
use crate::entities::{FieldType, SelectOptionCellDataPB, SelectOptionFilterPB}; use crate::entities::{FieldType, SelectOptionCellDataPB, SelectOptionFilterPB};
use crate::services::cell::CellDataChangeset; use crate::services::cell::CellDataChangeset;
use crate::services::field::{ use crate::services::field::{
default_order, SelectOption, TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, default_order, TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter,
TypeOptionCellDataSerde, TypeOptionCellDataSerde,
}; };
use crate::services::field::{ use crate::services::field::{
@ -9,6 +9,7 @@ use crate::services::field::{
}; };
use crate::services::sort::SortCondition; use crate::services::sort::SortCondition;
use collab::util::AnyMapExt; use collab::util::AnyMapExt;
use collab_database::entity::SelectOption;
use collab_database::fields::{TypeOptionData, TypeOptionDataBuilder}; use collab_database::fields::{TypeOptionData, TypeOptionDataBuilder};
use collab_database::rows::Cell; use collab_database::rows::Cell;
use flowy_error::FlowyResult; use flowy_error::FlowyResult;
@ -149,40 +150,9 @@ impl TypeOptionCellDataCompare for SingleSelectTypeOption {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::entities::FieldType;
use crate::services::cell::CellDataChangeset; use crate::services::cell::CellDataChangeset;
use crate::services::field::type_options::*; use crate::services::field::type_options::*;
use collab_database::entity::SelectOption;
#[test]
fn single_select_transform_with_checkbox_type_option_test() {
let checkbox = CheckboxTypeOption::default();
let mut single_select = SingleSelectTypeOption::default();
single_select.transform_type_option(FieldType::Checkbox, checkbox.clone().into());
debug_assert_eq!(single_select.options.len(), 2);
// Already contain the yes/no option. It doesn't need to insert new options
single_select.transform_type_option(FieldType::Checkbox, checkbox.into());
debug_assert_eq!(single_select.options.len(), 2);
}
#[test]
fn single_select_transform_with_multi_select_type_option_test() {
let google = SelectOption::new("Google");
let facebook = SelectOption::new("Facebook");
let multi_select = MultiSelectTypeOption {
options: vec![google, facebook],
disable_color: false,
};
let mut single_select = SingleSelectTypeOption::default();
single_select.transform_type_option(FieldType::MultiSelect, multi_select.clone().into());
debug_assert_eq!(single_select.options.len(), 2);
// Already contain the yes/no option. It doesn't need to insert new options
single_select.transform_type_option(FieldType::MultiSelect, multi_select.into());
debug_assert_eq!(single_select.options.len(), 2);
}
#[test] #[test]
fn single_select_insert_multi_option_test() { fn single_select_insert_multi_option_test() {

View File

@ -1,9 +1,13 @@
use crate::entities::FieldType; use crate::entities::FieldType;
use crate::services::cell::CellDataDecoder;
use crate::services::field::{ use crate::services::field::{
MultiSelectTypeOption, SelectOption, SelectOptionColor, SelectOptionIds, MultiSelectTypeOption, RichTextTypeOption, SelectOptionIds, SelectTypeOptionSharedAction,
SelectTypeOptionSharedAction, SingleSelectTypeOption, TypeOption, CHECK, UNCHECK, SingleSelectTypeOption, TypeOption, CHECK, UNCHECK,
}; };
use collab_database::database::Database;
use collab_database::entity::{SelectOption, SelectOptionColor};
use collab_database::fields::TypeOptionData; use collab_database::fields::TypeOptionData;
use collab_database::template::option_parse::build_options_from_cells;
/// Handles how to transform the cell data when switching between different field types /// Handles how to transform the cell data when switching between different field types
pub(crate) struct SelectOptionTypeOptionTransformHelper(); pub(crate) struct SelectOptionTypeOptionTransformHelper();
@ -14,14 +18,37 @@ impl SelectOptionTypeOptionTransformHelper {
/// ///
/// * `old_field_type`: the FieldType of the passed-in TypeOptionData /// * `old_field_type`: the FieldType of the passed-in TypeOptionData
/// ///
pub fn transform_type_option<T>( pub async fn transform_type_option<T>(
shared: &mut T, shared: &mut T,
view_id: &str,
field_id: &str,
old_field_type: &FieldType, old_field_type: &FieldType,
old_type_option_data: TypeOptionData, old_type_option_data: TypeOptionData,
database: &Database,
) where ) where
T: SelectTypeOptionSharedAction + TypeOption<CellData = SelectOptionIds>, T: SelectTypeOptionSharedAction + TypeOption<CellData = SelectOptionIds>,
{ {
match old_field_type { match old_field_type {
FieldType::RichText => {
if !shared.options().is_empty() {
return;
}
let text_type_option = RichTextTypeOption::from(old_type_option_data);
let cells = database
.get_cells_for_field(view_id, field_id)
.await
.into_iter()
.filter_map(|e| e.cell)
.map(|cell| {
text_type_option
.decode_cell(&cell)
.unwrap_or_default()
.into_inner()
})
.collect::<Vec<_>>();
let options = build_options_from_cells(&cells);
shared.mut_options().extend(options);
},
FieldType::Checkbox => { FieldType::Checkbox => {
// add Yes and No options if it does not exist. // add Yes and No options if it does not exist.
if !shared.options().iter().any(|option| option.name == CHECK) { if !shared.options().iter().any(|option| option.name == CHECK) {

View File

@ -4,6 +4,7 @@ mod tests {
use crate::services::cell::{insert_select_option_cell, stringify_cell}; use crate::services::cell::{insert_select_option_cell, stringify_cell};
use crate::services::field::FieldBuilder; use crate::services::field::FieldBuilder;
use crate::services::field::*; use crate::services::field::*;
use collab_database::entity::SelectOption;
// Test parser the cell data which field's type is FieldType::Date to cell data // Test parser the cell data which field's type is FieldType::Date to cell data
// which field's type is FieldType::Text // which field's type is FieldType::Text

View File

@ -147,6 +147,11 @@ impl TypeOptionCellDataCompare for RichTextTypeOption {
#[derive(Default, Debug, Clone)] #[derive(Default, Debug, Clone)]
pub struct StringCellData(pub String); pub struct StringCellData(pub String);
impl StringCellData {
pub fn into_inner(self) -> String {
self.0
}
}
impl std::ops::Deref for StringCellData { impl std::ops::Deref for StringCellData {
type Target = String; type Target = String;

View File

@ -1,10 +1,11 @@
use std::cmp::Ordering; use async_trait::async_trait;
use std::fmt::Debug;
use bytes::Bytes; use bytes::Bytes;
use collab_database::database::Database;
use collab_database::fields::TypeOptionData; use collab_database::fields::TypeOptionData;
use collab_database::rows::Cell; use collab_database::rows::Cell;
use protobuf::ProtobufError; use protobuf::ProtobufError;
use std::cmp::Ordering;
use std::fmt::Debug;
use flowy_error::FlowyResult; use flowy_error::FlowyResult;
@ -92,7 +93,8 @@ pub trait TypeOptionCellData {
} }
} }
pub trait TypeOptionTransform: TypeOption { #[async_trait]
pub trait TypeOptionTransform: TypeOption + Send + Sync {
/// Transform the TypeOption from one field type to another /// Transform the TypeOption from one field type to another
/// For example, when switching from `Checkbox` type option to `Single-Select` /// For example, when switching from `Checkbox` type option to `Single-Select`
/// type option, adding the `Yes` option if the `Single-select` type-option doesn't contain it. /// type option, adding the `Yes` option if the `Single-select` type-option doesn't contain it.
@ -104,10 +106,13 @@ pub trait TypeOptionTransform: TypeOption {
/// * `old_type_option_field_type`: the FieldType of the passed-in TypeOption /// * `old_type_option_field_type`: the FieldType of the passed-in TypeOption
/// * `old_type_option_data`: the data that can be parsed into corresponding `TypeOption`. /// * `old_type_option_data`: the data that can be parsed into corresponding `TypeOption`.
/// ///
fn transform_type_option( async fn transform_type_option(
&mut self, &mut self,
_view_id: &str,
_field_id: &str,
_old_type_option_field_type: FieldType, _old_type_option_field_type: FieldType,
_old_type_option_data: TypeOptionData, _old_type_option_data: TypeOptionData,
_database: &Database,
) { ) {
} }
} }

View File

@ -4,9 +4,8 @@ use std::collections::hash_map::DefaultHasher;
use std::collections::HashMap; use std::collections::HashMap;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use collab_database::fields::{Field, TypeOptionData}; use collab_database::fields::Field;
use collab_database::rows::{get_field_type_from_cell, Cell, RowId}; use collab_database::rows::{get_field_type_from_cell, Cell, RowId};
use flowy_error::FlowyResult; use flowy_error::FlowyResult;
use lib_infra::box_any::BoxAny; use lib_infra::box_any::BoxAny;
@ -93,6 +92,7 @@ pub trait TypeOptionCellDataHandler: Send + Sync + 'static {
fn handle_is_cell_empty(&self, cell: &Cell, field: &Field) -> bool; fn handle_is_cell_empty(&self, cell: &Cell, field: &Field) -> bool;
} }
#[derive(Debug)]
struct CellDataCacheKey(u64); struct CellDataCacheKey(u64);
impl CellDataCacheKey { impl CellDataCacheKey {
pub fn new(field_rev: &Field, decoded_field_type: FieldType, cell: &Cell) -> Self { pub fn new(field_rev: &Field, decoded_field_type: FieldType, cell: &Cell) -> Self {
@ -185,9 +185,7 @@ where
fn get_cell_data_from_cache(&self, cell: &Cell, field: &Field) -> Option<T::CellData> { fn get_cell_data_from_cache(&self, cell: &Cell, field: &Field) -> Option<T::CellData> {
let key = self.get_cell_data_cache_key(cell, field); let key = self.get_cell_data_cache_key(cell, field);
let cell_data_cache = self.cell_data_cache.as_ref()?; let cell_data_cache = self.cell_data_cache.as_ref()?;
let cell = cell_data_cache.get::<T::CellData>(key.as_ref())?; let cell = cell_data_cache.get::<T::CellData>(key.as_ref())?;
Some(cell.value().clone()) Some(cell.value().clone())
} }
@ -507,109 +505,27 @@ impl<'a> TypeOptionCellExt<'a> {
} }
} }
/// when return true, the to_field_type must implement [CellDataDecoder]'s decode_cell_with_transform
pub fn is_type_option_cell_transformable( pub fn is_type_option_cell_transformable(
from_field_type: FieldType, from_field_type: FieldType,
to_field_type: FieldType, to_field_type: FieldType,
) -> bool { ) -> bool {
matches!( matches!(
(from_field_type, to_field_type), (from_field_type, to_field_type),
// Checkbox
(FieldType::Checkbox, FieldType::SingleSelect) (FieldType::Checkbox, FieldType::SingleSelect)
| (FieldType::Checkbox, FieldType::MultiSelect) | (FieldType::Checkbox, FieldType::MultiSelect)
// SingleSelect or MultiSelect
| (FieldType::SingleSelect, FieldType::MultiSelect) | (FieldType::SingleSelect, FieldType::MultiSelect)
| (FieldType::MultiSelect, FieldType::SingleSelect) | (FieldType::MultiSelect, FieldType::SingleSelect)
// Text
| (FieldType::RichText, FieldType::SingleSelect)
| (FieldType::RichText, FieldType::MultiSelect)
| (FieldType::RichText, FieldType::URL)
| (_, FieldType::RichText) | (_, FieldType::RichText)
) )
} }
pub fn transform_type_option(
old_field_type: FieldType,
new_field_type: FieldType,
old_type_option_data: Option<TypeOptionData>,
new_type_option_data: TypeOptionData,
) -> TypeOptionData {
if let Some(old_type_option_data) = old_type_option_data {
let mut transform_handler =
get_type_option_transform_handler(new_type_option_data, new_field_type);
transform_handler.transform(old_field_type, old_type_option_data);
transform_handler.to_type_option_data()
} else {
new_type_option_data
}
}
/// A helper trait that used to erase the `Self` of `TypeOption` trait to make it become a Object-safe trait.
pub trait TypeOptionTransformHandler {
fn transform(
&mut self,
old_type_option_field_type: FieldType,
old_type_option_data: TypeOptionData,
);
fn to_type_option_data(&self) -> TypeOptionData;
}
impl<T> TypeOptionTransformHandler for T
where
T: TypeOptionTransform + Clone,
{
fn transform(
&mut self,
old_type_option_field_type: FieldType,
old_type_option_data: TypeOptionData,
) {
self.transform_type_option(old_type_option_field_type, old_type_option_data)
}
fn to_type_option_data(&self) -> TypeOptionData {
self.clone().into()
}
}
fn get_type_option_transform_handler(
type_option_data: TypeOptionData,
field_type: FieldType,
) -> Box<dyn TypeOptionTransformHandler> {
match field_type {
FieldType::RichText => {
Box::new(RichTextTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::Number => {
Box::new(NumberTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::DateTime => {
Box::new(DateTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::LastEditedTime | FieldType::CreatedTime => {
Box::new(TimestampTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::SingleSelect => Box::new(SingleSelectTypeOption::from(type_option_data))
as Box<dyn TypeOptionTransformHandler>,
FieldType::MultiSelect => {
Box::new(MultiSelectTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::Checkbox => {
Box::new(CheckboxTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::URL => {
Box::new(URLTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::Checklist => {
Box::new(ChecklistTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::Relation => {
Box::new(RelationTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::Summary => Box::new(SummarizationTypeOption::from(type_option_data))
as Box<dyn TypeOptionTransformHandler>,
FieldType::Time => {
Box::new(TimeTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::Translate => {
Box::new(TranslateTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
}
}
pub type BoxCellData = BoxAny; pub type BoxCellData = BoxAny;
pub struct RowSingleCellData { pub struct RowSingleCellData {

View File

@ -1,13 +1,13 @@
use collab::preclude::encoding::serde::from_any; use collab::preclude::encoding::serde::from_any;
use collab::preclude::Any; use collab::preclude::Any;
use std::cmp::Ordering;
use collab_database::fields::{TypeOptionData, TypeOptionDataBuilder}; use collab_database::fields::{Field, TypeOptionData, TypeOptionDataBuilder};
use collab_database::rows::Cell; use collab_database::rows::Cell;
use flowy_error::FlowyResult; use flowy_error::FlowyResult;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use crate::entities::{TextFilterPB, URLCellDataPB}; use crate::entities::{FieldType, TextFilterPB, URLCellDataPB};
use crate::services::cell::{CellDataChangeset, CellDataDecoder}; use crate::services::cell::{CellDataChangeset, CellDataDecoder};
use crate::services::field::{ use crate::services::field::{
TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, TypeOptionCellDataSerde, TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, TypeOptionCellDataSerde,
@ -64,6 +64,17 @@ impl CellDataDecoder for URLTypeOption {
fn decode_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> { fn decode_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
self.parse_cell(cell) self.parse_cell(cell)
} }
fn decode_cell_with_transform(
&self,
cell: &Cell,
from_field_type: FieldType,
_field: &Field,
) -> Option<<Self as TypeOption>::CellData> {
match from_field_type {
FieldType::RichText => Some(Self::CellData::from(cell)),
_ => None,
}
}
fn stringify_cell_data(&self, cell_data: <Self as TypeOption>::CellData) -> String { fn stringify_cell_data(&self, cell_data: <Self as TypeOption>::CellData) -> String {
cell_data.data cell_data.data

View File

@ -360,7 +360,8 @@ impl FilterController {
if is_visible { if is_visible {
if let Some((index, _row)) = self.delegate.get_row(&self.view_id, &row_id).await { if let Some((index, _row)) = self.delegate.get_row(&self.view_id, &row_id).await {
notification.visible_rows.push( notification.visible_rows.push(
InsertedRowPB::new(RowMetaPB::from(row_detail.as_ref())).with_index(index as i32), InsertedRowPB::new(RowMetaPB::from(row_detail.as_ref().clone()))
.with_index(index as i32),
) )
} }
} else { } else {
@ -397,7 +398,7 @@ impl FilterController {
&filters, &filters,
) { ) {
if is_visible { if is_visible {
let row_meta = RowMetaPB::from(row_detail.as_ref()); let row_meta = RowMetaPB::from(row_detail.as_ref().clone());
visible_rows.push(InsertedRowPB::new(row_meta).with_index(index as i32)) visible_rows.push(InsertedRowPB::new(row_meta).with_index(index as i32))
} else { } else {
invisible_rows.push(row_detail.row.id.clone()); invisible_rows.push(row_detail.row.id.clone());

View File

@ -115,7 +115,7 @@ where
if !no_status_group_rows.is_empty() { if !no_status_group_rows.is_empty() {
changeset changeset
.inserted_rows .inserted_rows
.push(InsertedRowPB::new(RowMetaPB::from(row_detail))); .push(InsertedRowPB::new(RowMetaPB::from(row_detail.clone())));
no_status_group.add_row(row_detail.clone()); no_status_group.add_row(row_detail.clone());
} }

View File

@ -68,7 +68,7 @@ impl GroupCustomize for CheckboxGroupController {
if is_not_contained { if is_not_contained {
changeset changeset
.inserted_rows .inserted_rows
.push(InsertedRowPB::new(RowMetaPB::from(row_detail))); .push(InsertedRowPB::new(RowMetaPB::from(row_detail.clone())));
group.add_row(row_detail.clone()); group.add_row(row_detail.clone());
} }
} }
@ -86,7 +86,7 @@ impl GroupCustomize for CheckboxGroupController {
if is_not_contained { if is_not_contained {
changeset changeset
.inserted_rows .inserted_rows
.push(InsertedRowPB::new(RowMetaPB::from(row_detail))); .push(InsertedRowPB::new(RowMetaPB::from(row_detail.clone())));
group.add_row(row_detail.clone()); group.add_row(row_detail.clone());
} }
} }

View File

@ -86,7 +86,10 @@ impl GroupCustomize for DateGroupController {
{ {
let group = make_group_from_date_cell(&_cell_data.into(), &setting_content); let group = make_group_from_date_cell(&_cell_data.into(), &setting_content);
let mut new_group = self.context.add_new_group(group)?; let mut new_group = self.context.add_new_group(group)?;
new_group.group.rows.push(RowMetaPB::from(_row_detail)); new_group
.group
.rows
.push(RowMetaPB::from(_row_detail.clone()));
inserted_group = Some(new_group); inserted_group = Some(new_group);
} }
@ -130,7 +133,7 @@ impl GroupCustomize for DateGroupController {
if !group.contains_row(&row_detail.row.id) { if !group.contains_row(&row_detail.row.id) {
changeset changeset
.inserted_rows .inserted_rows
.push(InsertedRowPB::new(RowMetaPB::from(row_detail))); .push(InsertedRowPB::new(RowMetaPB::from(row_detail.clone())));
group.add_row(row_detail.clone()); group.add_row(row_detail.clone());
} }
} else if group.contains_row(&row_detail.row.id) { } else if group.contains_row(&row_detail.row.id) {

View File

@ -1,4 +1,5 @@
use async_trait::async_trait; use async_trait::async_trait;
use collab_database::entity::SelectOption;
use collab_database::fields::{Field, TypeOptionData}; use collab_database::fields::{Field, TypeOptionData};
use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail}; use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail};
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::{FlowyError, FlowyResult};
@ -7,8 +8,7 @@ use serde::{Deserialize, Serialize};
use crate::entities::{FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB}; use crate::entities::{FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB};
use crate::services::cell::insert_select_option_cell; use crate::services::cell::insert_select_option_cell;
use crate::services::field::{ use crate::services::field::{
MultiSelectTypeOption, SelectOption, SelectOptionCellDataParser, SelectTypeOptionSharedAction, MultiSelectTypeOption, SelectOptionCellDataParser, SelectTypeOptionSharedAction, TypeOption,
TypeOption,
}; };
use crate::services::group::action::GroupCustomize; use crate::services::group::action::GroupCustomize;
use crate::services::group::controller::BaseGroupController; use crate::services::group::controller::BaseGroupController;

View File

@ -1,4 +1,5 @@
use async_trait::async_trait; use async_trait::async_trait;
use collab_database::entity::SelectOption;
use collab_database::fields::{Field, TypeOptionData}; use collab_database::fields::{Field, TypeOptionData};
use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail}; use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail};
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::{FlowyError, FlowyResult};
@ -7,8 +8,7 @@ use serde::{Deserialize, Serialize};
use crate::entities::{FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB}; use crate::entities::{FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB};
use crate::services::cell::insert_select_option_cell; use crate::services::cell::insert_select_option_cell;
use crate::services::field::{ use crate::services::field::{
SelectOption, SelectOptionCellDataParser, SelectTypeOptionSharedAction, SingleSelectTypeOption, SelectOptionCellDataParser, SelectTypeOptionSharedAction, SingleSelectTypeOption, TypeOption,
TypeOption,
}; };
use crate::services::group::action::GroupCustomize; use crate::services::group::action::GroupCustomize;
use crate::services::group::controller::BaseGroupController; use crate::services::group::controller::BaseGroupController;

View File

@ -1,4 +1,5 @@
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use collab_database::entity::SelectOption;
use collab_database::fields::Field; use collab_database::fields::Field;
use collab_database::rows::{Cell, Row, RowDetail}; use collab_database::rows::{Cell, Row, RowDetail};
@ -8,7 +9,7 @@ use crate::entities::{
use crate::services::cell::{ use crate::services::cell::{
insert_checkbox_cell, insert_date_cell, insert_select_option_cell, insert_url_cell, insert_checkbox_cell, insert_date_cell, insert_select_option_cell, insert_url_cell,
}; };
use crate::services::field::{SelectOption, SelectOptionIds, CHECK}; use crate::services::field::{SelectOptionIds, CHECK};
use crate::services::group::{Group, GroupData, MoveGroupRowContext}; use crate::services::group::{Group, GroupData, MoveGroupRowContext};
pub fn add_or_remove_select_option_row( pub fn add_or_remove_select_option_row(
@ -30,7 +31,7 @@ pub fn add_or_remove_select_option_row(
if !group.contains_row(&row_detail.row.id) { if !group.contains_row(&row_detail.row.id) {
changeset changeset
.inserted_rows .inserted_rows
.push(InsertedRowPB::new(RowMetaPB::from(row_detail))); .push(InsertedRowPB::new(RowMetaPB::from(row_detail.clone())));
group.add_row(row_detail.clone()); group.add_row(row_detail.clone());
} }
} else if group.contains_row(&row_detail.row.id) { } else if group.contains_row(&row_detail.row.id) {

View File

@ -57,7 +57,10 @@ impl GroupCustomize for URLGroupController {
let cell_data: URLCellData = _cell_data.clone().into(); let cell_data: URLCellData = _cell_data.clone().into();
let group = Group::new(cell_data.data); let group = Group::new(cell_data.data);
let mut new_group = self.context.add_new_group(group)?; let mut new_group = self.context.add_new_group(group)?;
new_group.group.rows.push(RowMetaPB::from(_row_detail)); new_group
.group
.rows
.push(RowMetaPB::from(_row_detail.clone()));
inserted_group = Some(new_group); inserted_group = Some(new_group);
} }
@ -98,7 +101,7 @@ impl GroupCustomize for URLGroupController {
if !group.contains_row(&row_detail.row.id) { if !group.contains_row(&row_detail.row.id) {
changeset changeset
.inserted_rows .inserted_rows
.push(InsertedRowPB::new(RowMetaPB::from(row_detail))); .push(InsertedRowPB::new(RowMetaPB::from(row_detail.clone())));
group.add_row(row_detail.clone()); group.add_row(row_detail.clone());
} }
} else if group.contains_row(&row_detail.row.id) { } else if group.contains_row(&row_detail.row.id) {

View File

@ -1,13 +1,13 @@
use collab_database::database::{gen_database_id, gen_row_id, timestamp}; use collab_database::database::{gen_database_id, gen_row_id, timestamp};
use collab_database::entity::{CreateDatabaseParams, CreateViewParams}; use collab_database::entity::{
CreateDatabaseParams, CreateViewParams, SelectOption, SelectOptionColor,
};
use collab_database::rows::CreateRowParams; use collab_database::rows::CreateRowParams;
use collab_database::views::{DatabaseLayout, LayoutSettings}; use collab_database::views::{DatabaseLayout, LayoutSettings};
use crate::entities::FieldType; use crate::entities::FieldType;
use crate::services::cell::{insert_select_option_cell, insert_text_cell}; use crate::services::cell::{insert_select_option_cell, insert_text_cell};
use crate::services::field::{ use crate::services::field::{FieldBuilder, SingleSelectTypeOption};
FieldBuilder, SelectOption, SelectOptionColor, SingleSelectTypeOption,
};
use crate::services::field_settings::default_field_settings_for_fields; use crate::services::field_settings::default_field_settings_for_fields;
use crate::services::setting::{BoardLayoutSetting, CalendarLayoutSetting}; use crate::services::setting::{BoardLayoutSetting, CalendarLayoutSetting};

View File

@ -2,6 +2,7 @@ use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use collab_database::database::gen_database_view_id; use collab_database::database::gen_database_view_id;
use collab_database::entity::SelectOption;
use collab_database::fields::Field; use collab_database::fields::Field;
use collab_database::rows::{RowDetail, RowId}; use collab_database::rows::{RowDetail, RowId};
use lib_infra::box_any::BoxAny; use lib_infra::box_any::BoxAny;
@ -16,8 +17,7 @@ use flowy_database2::services::field::checklist_type_option::{
ChecklistCellChangeset, ChecklistTypeOption, ChecklistCellChangeset, ChecklistTypeOption,
}; };
use flowy_database2::services::field::{ use flowy_database2::services::field::{
CheckboxTypeOption, MultiSelectTypeOption, SelectOption, SelectOptionCellChangeset, CheckboxTypeOption, MultiSelectTypeOption, SelectOptionCellChangeset, SingleSelectTypeOption,
SingleSelectTypeOption,
}; };
use flowy_database2::services::share::csv::{CSVFormat, ImportResult}; use flowy_database2::services::share::csv::{CSVFormat, ImportResult};
use flowy_error::FlowyResult; use flowy_error::FlowyResult;
@ -86,7 +86,7 @@ impl DatabaseEditorTest {
.map(Arc::new) .map(Arc::new)
.collect(); .collect();
let rows = editor let rows = editor
.get_row_details(&test.child_view.id) .get_all_row_details(&test.child_view.id)
.await .await
.unwrap() .unwrap()
.into_iter() .into_iter()
@ -109,7 +109,11 @@ impl DatabaseEditorTest {
} }
pub async fn get_rows(&self) -> Vec<Arc<RowDetail>> { pub async fn get_rows(&self) -> Vec<Arc<RowDetail>> {
self.editor.get_row_details(&self.view_id).await.unwrap() self
.editor
.get_all_row_details(&self.view_id)
.await
.unwrap()
} }
pub async fn get_field(&self, field_id: &str, field_type: FieldType) -> Field { pub async fn get_field(&self, field_id: &str, field_type: FieldType) -> Field {

View File

@ -16,6 +16,7 @@ pub enum FieldScript {
field: Field, field: Field,
}, },
SwitchToField { SwitchToField {
view_id: String,
field_id: String, field_id: String,
new_field_type: FieldType, new_field_type: FieldType,
}, },
@ -80,13 +81,14 @@ impl DatabaseFieldTest {
assert_eq!(self.field_count, fields.len()); assert_eq!(self.field_count, fields.len());
}, },
FieldScript::SwitchToField { FieldScript::SwitchToField {
view_id,
field_id, field_id,
new_field_type, new_field_type,
} => { } => {
// //
self self
.editor .editor
.switch_to_field_type(&field_id, new_field_type) .switch_to_field_type(&view_id, &field_id, new_field_type)
.await .await
.unwrap(); .unwrap();
}, },
@ -121,7 +123,11 @@ impl DatabaseFieldTest {
} => { } => {
let field = self.editor.get_field(&field_id).await.unwrap(); let field = self.editor.get_field(&field_id).await.unwrap();
let rows = self.editor.get_row_details(&self.view_id()).await.unwrap(); let rows = self
.editor
.get_all_row_details(&self.view_id())
.await
.unwrap();
let row_detail = rows.get(row_index).unwrap(); let row_detail = rows.get(row_index).unwrap();
let cell = row_detail.row.cells.get(&field_id).unwrap().clone(); let cell = row_detail.row.cells.get(&field_id).unwrap().clone();

View File

@ -1,7 +1,7 @@
use collab_database::database::gen_option_id; use collab_database::database::gen_option_id;
use collab_database::entity::SelectOption;
use flowy_database2::entities::{FieldChangesetParams, FieldType}; use flowy_database2::entities::{FieldChangesetParams, FieldType};
use flowy_database2::services::field::{SelectOption, SingleSelectTypeOption, CHECK, UNCHECK}; use flowy_database2::services::field::{SingleSelectTypeOption, CHECK, UNCHECK};
use crate::database::field_test::script::DatabaseFieldTest; use crate::database::field_test::script::DatabaseFieldTest;
use crate::database::field_test::script::FieldScript::*; use crate::database::field_test::script::FieldScript::*;
@ -122,6 +122,7 @@ async fn grid_delete_field() {
async fn grid_switch_from_select_option_to_checkbox_test() { async fn grid_switch_from_select_option_to_checkbox_test() {
let mut test = DatabaseFieldTest::new().await; let mut test = DatabaseFieldTest::new().await;
let field = test.get_first_field(FieldType::SingleSelect).await; let field = test.get_first_field(FieldType::SingleSelect).await;
let view_id = test.view_id();
// Update the type option data of single select option // Update the type option data of single select option
let mut options = test.get_single_select_type_option(&field.id).await; let mut options = test.get_single_select_type_option(&field.id).await;
@ -149,6 +150,7 @@ async fn grid_switch_from_select_option_to_checkbox_test() {
.into(), .into(),
}, },
SwitchToField { SwitchToField {
view_id: view_id.clone(),
field_id: field.id.clone(), field_id: field.id.clone(),
new_field_type: FieldType::Checkbox, new_field_type: FieldType::Checkbox,
}, },
@ -163,6 +165,7 @@ async fn grid_switch_from_checkbox_to_select_option_test() {
let scripts = vec![ let scripts = vec![
// switch to single-select field type // switch to single-select field type
SwitchToField { SwitchToField {
view_id: test.view_id(),
field_id: checkbox_field.id.clone(), field_id: checkbox_field.id.clone(),
new_field_type: FieldType::SingleSelect, new_field_type: FieldType::SingleSelect,
}, },
@ -199,6 +202,7 @@ async fn grid_switch_from_multi_select_to_text_test() {
let multi_select_type_option = test.get_multi_select_type_option(&field_rev.id).await; let multi_select_type_option = test.get_multi_select_type_option(&field_rev.id).await;
let script_switch_field = vec![SwitchToField { let script_switch_field = vec![SwitchToField {
view_id: test.view_id(),
field_id: field_rev.id.clone(), field_id: field_rev.id.clone(),
new_field_type: FieldType::RichText, new_field_type: FieldType::RichText,
}]; }];
@ -229,6 +233,7 @@ async fn grid_switch_from_checkbox_to_text_test() {
let scripts = vec![ let scripts = vec![
SwitchToField { SwitchToField {
view_id: test.view_id(),
field_id: field_rev.id.clone(), field_id: field_rev.id.clone(),
new_field_type: FieldType::RichText, new_field_type: FieldType::RichText,
}, },
@ -255,6 +260,7 @@ async fn grid_switch_from_date_to_text_test() {
let field = test.get_first_field(FieldType::DateTime).await.clone(); let field = test.get_first_field(FieldType::DateTime).await.clone();
let scripts = vec![ let scripts = vec![
SwitchToField { SwitchToField {
view_id: test.view_id(),
field_id: field.id.clone(), field_id: field.id.clone(),
new_field_type: FieldType::RichText, new_field_type: FieldType::RichText,
}, },
@ -282,6 +288,7 @@ async fn grid_switch_from_number_to_text_test() {
let scripts = vec![ let scripts = vec![
SwitchToField { SwitchToField {
view_id: test.view_id(),
field_id: field.id.clone(), field_id: field.id.clone(),
new_field_type: FieldType::RichText, new_field_type: FieldType::RichText,
}, },
@ -308,6 +315,7 @@ async fn grid_switch_from_checklist_to_text_test() {
let scripts = vec![ let scripts = vec![
SwitchToField { SwitchToField {
view_id: test.view_id(),
field_id: field_rev.id.clone(), field_id: field_rev.id.clone(),
new_field_type: FieldType::RichText, new_field_type: FieldType::RichText,
}, },

View File

@ -1,9 +1,10 @@
use collab_database::entity::SelectOption;
use collab_database::fields::Field; use collab_database::fields::Field;
use collab_database::views::OrderObjectPosition; use collab_database::views::OrderObjectPosition;
use flowy_database2::entities::{CreateFieldParams, FieldType}; use flowy_database2::entities::{CreateFieldParams, FieldType};
use flowy_database2::services::field::{ use flowy_database2::services::field::{
type_option_to_pb, DateFormat, DateTypeOption, FieldBuilder, RichTextTypeOption, SelectOption, type_option_to_pb, DateFormat, DateTypeOption, FieldBuilder, RichTextTypeOption,
SingleSelectTypeOption, TimeFormat, TimeTypeOption, TimestampTypeOption, SingleSelectTypeOption, TimeFormat, TimeTypeOption, TimestampTypeOption,
}; };

View File

@ -301,7 +301,7 @@ impl DatabaseFilterTest {
} }
}, },
FilterScript::AssertNumberOfVisibleRows { expected } => { FilterScript::AssertNumberOfVisibleRows { expected } => {
let grid = self.editor.get_database_data(&self.view_id).await.unwrap(); let grid = self.editor.open_database(&self.view_id).await.unwrap();
assert_eq!(grid.rows.len(), expected); assert_eq!(grid.rows.len(), expected);
}, },
FilterScript::Wait { millisecond } => { FilterScript::Wait { millisecond } => {

View File

@ -1,3 +1,4 @@
use collab_database::entity::SelectOption;
use collab_database::fields::Field; use collab_database::fields::Field;
use collab_database::rows::RowId; use collab_database::rows::RowId;
@ -6,8 +7,7 @@ use flowy_database2::services::cell::{
delete_select_option_cell, insert_date_cell, insert_select_option_cell, insert_url_cell, delete_select_option_cell, insert_date_cell, insert_select_option_cell, insert_url_cell,
}; };
use flowy_database2::services::field::{ use flowy_database2::services::field::{
edit_single_select_type_option, SelectOption, SelectTypeOptionSharedAction, edit_single_select_type_option, SelectTypeOptionSharedAction, SingleSelectTypeOption,
SingleSelectTypeOption,
}; };
use crate::database::database_editor::DatabaseEditorTest; use crate::database::database_editor::DatabaseEditorTest;

View File

@ -1,7 +1,6 @@
use flowy_database2::services::field::SelectOption;
use crate::database::group_test::script::DatabaseGroupTest; use crate::database::group_test::script::DatabaseGroupTest;
use crate::database::group_test::script::GroupScript::*; use crate::database::group_test::script::GroupScript::*;
use collab_database::entity::SelectOption;
#[tokio::test] #[tokio::test]
async fn group_init_test() { async fn group_init_test() {

View File

@ -1,5 +1,5 @@
use collab_database::database::{gen_database_id, gen_database_view_id, gen_row_id, DatabaseData}; use collab_database::database::{gen_database_id, gen_database_view_id, gen_row_id, DatabaseData};
use collab_database::entity::DatabaseView; use collab_database::entity::{DatabaseView, SelectOption, SelectOptionColor};
use collab_database::views::{DatabaseLayout, LayoutSetting, LayoutSettings}; use collab_database::views::{DatabaseLayout, LayoutSetting, LayoutSettings};
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
@ -10,7 +10,7 @@ use flowy_database2::services::field::checklist_type_option::ChecklistTypeOption
use flowy_database2::services::field::summary_type_option::summary::SummarizationTypeOption; use flowy_database2::services::field::summary_type_option::summary::SummarizationTypeOption;
use flowy_database2::services::field::{ use flowy_database2::services::field::{
DateFormat, DateTypeOption, FieldBuilder, MultiSelectTypeOption, RelationTypeOption, DateFormat, DateTypeOption, FieldBuilder, MultiSelectTypeOption, RelationTypeOption,
SelectOption, SelectOptionColor, SingleSelectTypeOption, TimeFormat, TimestampTypeOption, SingleSelectTypeOption, TimeFormat, TimestampTypeOption,
}; };
use flowy_database2::services::field_settings::default_field_settings_for_fields; use flowy_database2::services::field_settings::default_field_settings_for_fields;
use flowy_database2::services::setting::BoardLayoutSetting; use flowy_database2::services::setting::BoardLayoutSetting;

View File

@ -1,5 +1,5 @@
use collab_database::database::{gen_database_id, gen_database_view_id, gen_row_id, DatabaseData}; use collab_database::database::{gen_database_id, gen_database_view_id, gen_row_id, DatabaseData};
use collab_database::entity::DatabaseView; use collab_database::entity::{DatabaseView, SelectOption, SelectOptionColor};
use collab_database::views::DatabaseLayout; use collab_database::views::DatabaseLayout;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
@ -10,8 +10,8 @@ use flowy_database2::services::field::summary_type_option::summary::Summarizatio
use flowy_database2::services::field::translate_type_option::translate::TranslateTypeOption; use flowy_database2::services::field::translate_type_option::translate::TranslateTypeOption;
use flowy_database2::services::field::{ use flowy_database2::services::field::{
ChecklistTypeOption, DateFormat, DateTypeOption, FieldBuilder, MultiSelectTypeOption, ChecklistTypeOption, DateFormat, DateTypeOption, FieldBuilder, MultiSelectTypeOption,
NumberFormat, NumberTypeOption, RelationTypeOption, SelectOption, SelectOptionColor, NumberFormat, NumberTypeOption, RelationTypeOption, SingleSelectTypeOption, TimeFormat,
SingleSelectTypeOption, TimeFormat, TimeTypeOption, TimestampTypeOption, TimeTypeOption, TimestampTypeOption,
}; };
use flowy_database2::services::field_settings::default_field_settings_for_fields; use flowy_database2::services::field_settings::default_field_settings_for_fields;

View File

@ -86,7 +86,11 @@ impl DatabasePreFillRowCellTest {
.await .await
.unwrap(), .unwrap(),
PreFillRowCellTestScript::AssertRowCount(expected_row_count) => { PreFillRowCellTestScript::AssertRowCount(expected_row_count) => {
let rows = self.editor.get_row_details(&self.view_id).await.unwrap(); let rows = self
.editor
.get_all_row_details(&self.view_id)
.await
.unwrap();
assert_eq!(expected_row_count, rows.len()); assert_eq!(expected_row_count, rows.len());
}, },
PreFillRowCellTestScript::AssertCellExistence { PreFillRowCellTestScript::AssertCellExistence {
@ -94,7 +98,11 @@ impl DatabasePreFillRowCellTest {
row_index, row_index,
exists, exists,
} => { } => {
let rows = self.editor.get_row_details(&self.view_id).await.unwrap(); let rows = self
.editor
.get_all_row_details(&self.view_id)
.await
.unwrap();
let row_detail = rows.get(row_index).unwrap(); let row_detail = rows.get(row_index).unwrap();
let cell = row_detail.row.cells.get(&field_id).cloned(); let cell = row_detail.row.cells.get(&field_id).cloned();
@ -108,7 +116,11 @@ impl DatabasePreFillRowCellTest {
} => { } => {
let field = self.editor.get_field(&field_id).await.unwrap(); let field = self.editor.get_field(&field_id).await.unwrap();
let rows = self.editor.get_row_details(&self.view_id).await.unwrap(); let rows = self
.editor
.get_all_row_details(&self.view_id)
.await
.unwrap();
let row_detail = rows.get(row_index).unwrap(); let row_detail = rows.get(row_index).unwrap();
let cell = row_detail let cell = row_detail
@ -125,7 +137,11 @@ impl DatabasePreFillRowCellTest {
row_index, row_index,
expected_content, expected_content,
} => { } => {
let rows = self.editor.get_row_details(&self.view_id).await.unwrap(); let rows = self
.editor
.get_all_row_details(&self.view_id)
.await
.unwrap();
let row_detail = rows.get(row_index).unwrap(); let row_detail = rows.get(row_index).unwrap();
let cell = row_detail let cell = row_detail

View File

@ -33,7 +33,7 @@ async fn export_and_then_import_meta_csv_test() {
let database = test.get_database(&result.database_id).await.unwrap(); let database = test.get_database(&result.database_id).await.unwrap();
let fields = database.get_fields(&result.view_id, None).await; let fields = database.get_fields(&result.view_id, None).await;
let rows = database.get_row_details(&result.view_id).await.unwrap(); let rows = database.get_all_row_details(&result.view_id).await.unwrap();
assert_eq!(fields[0].field_type, 0); assert_eq!(fields[0].field_type, 0);
assert_eq!(fields[1].field_type, 1); assert_eq!(fields[1].field_type, 1);
assert_eq!(fields[2].field_type, 2); assert_eq!(fields[2].field_type, 2);
@ -112,7 +112,7 @@ async fn history_database_import_test() {
let database = test.get_database(&result.database_id).await.unwrap(); let database = test.get_database(&result.database_id).await.unwrap();
let fields = database.get_fields(&result.view_id, None).await; let fields = database.get_fields(&result.view_id, None).await;
let rows = database.get_row_details(&result.view_id).await.unwrap(); let rows = database.get_all_row_details(&result.view_id).await.unwrap();
assert_eq!(fields[0].field_type, 0); assert_eq!(fields[0].field_type, 0);
assert_eq!(fields[1].field_type, 1); assert_eq!(fields[1].field_type, 1);
assert_eq!(fields[2].field_type, 2); assert_eq!(fields[2].field_type, 2);

View File

@ -117,7 +117,11 @@ impl DatabaseSortTest {
}, },
SortScript::AssertCellContentOrder { field_id, orders } => { SortScript::AssertCellContentOrder { field_id, orders } => {
let mut cells = vec![]; let mut cells = vec![];
let rows = self.editor.get_row_details(&self.view_id).await.unwrap(); let rows = self
.editor
.get_all_row_details(&self.view_id)
.await
.unwrap();
let field = self.editor.get_field(&field_id).await.unwrap(); let field = self.editor.get_field(&field_id).await.unwrap();
for row_detail in rows { for row_detail in rows {
if let Some(cell) = row_detail.row.cells.get(&field_id) { if let Some(cell) = row_detail.row.cells.get(&field_id) {