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

View File

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

View File

@ -2,9 +2,7 @@ import 'dart:async';
import 'dart:collection';
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/protobuf/flowy-database2/protobuf.dart';
import '../defines.dart';
import '../field/field_controller.dart';
@ -93,17 +91,6 @@ class DatabaseViewCache {
(reorderRow) => _rowCache.reorderSingleRow(reorderRow),
(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(

View File

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

View File

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

View File

@ -342,7 +342,7 @@ class _GridRowsState extends State<_GridRows> {
child: ReorderableListView.builder(
/// This is a workaround related to
/// https://github.com/flutter/flutter/issues/25652
cacheExtent: 5000,
cacheExtent: 600,
scrollController: widget.scrollController.verticalController,
physics: const ClampingScrollPhysics(),
buildDefaultDragHandles: false,
@ -421,7 +421,7 @@ class _GridRowsState extends State<_GridRows> {
);
final child = GridRow(
key: ValueKey(rowMeta.id),
key: ValueKey(rowId),
fieldController: databaseController.fieldController,
rowId: rowId,
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:flowy_infra/size.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:flutter/material.dart';
import 'package:flutter/services.dart';
@ -202,9 +203,15 @@ class _URLAccessoryIconContainer extends StatelessWidget {
),
borderRadius: Corners.s6Border,
),
child: FlowyHover(
style: HoverStyle(
backgroundColor: AFThemeExtension.of(context).background,
hoverColor: AFThemeExtension.of(context).lightGreyHover,
),
child: Center(
child: child,
),
),
);
}
}

View File

@ -964,7 +964,7 @@ dependencies = [
[[package]]
name = "collab"
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 = [
"anyhow",
"arc-swap",
@ -989,7 +989,7 @@ dependencies = [
[[package]]
name = "collab-database"
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 = [
"anyhow",
"async-trait",
@ -1018,7 +1018,7 @@ dependencies = [
[[package]]
name = "collab-document"
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 = [
"anyhow",
"arc-swap",
@ -1038,7 +1038,7 @@ dependencies = [
[[package]]
name = "collab-entity"
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 = [
"anyhow",
"bytes",
@ -1057,7 +1057,7 @@ dependencies = [
[[package]]
name = "collab-folder"
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 = [
"anyhow",
"arc-swap",
@ -1100,7 +1100,7 @@ dependencies = [
[[package]]
name = "collab-plugins"
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 = [
"anyhow",
"async-stream",
@ -1180,7 +1180,7 @@ dependencies = [
[[package]]
name = "collab-user"
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 = [
"anyhow",
"collab",

View File

@ -116,13 +116,13 @@ custom-protocol = ["tauri/custom-protocol"]
# To switch to the local path, run:
# scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️
collab = { 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 = "c714b6bd458420663c7a5b29370d6892902e995c" }
collab-folder = { 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 = "c714b6bd458420663c7a5b29370d6892902e995c" }
collab-database = { 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 = "c714b6bd458420663c7a5b29370d6892902e995c" }
collab-user = { 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 = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
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 = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
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 = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
# Working directory: frontend
# To update the commit ID, run:

View File

@ -947,7 +947,7 @@ dependencies = [
[[package]]
name = "collab"
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 = [
"anyhow",
"arc-swap",
@ -972,7 +972,7 @@ dependencies = [
[[package]]
name = "collab-database"
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 = [
"anyhow",
"async-trait",
@ -1001,7 +1001,7 @@ dependencies = [
[[package]]
name = "collab-document"
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 = [
"anyhow",
"arc-swap",
@ -1021,7 +1021,7 @@ dependencies = [
[[package]]
name = "collab-entity"
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 = [
"anyhow",
"bytes",
@ -1040,7 +1040,7 @@ dependencies = [
[[package]]
name = "collab-folder"
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 = [
"anyhow",
"arc-swap",
@ -1083,7 +1083,7 @@ dependencies = [
[[package]]
name = "collab-plugins"
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 = [
"anyhow",
"async-stream",
@ -1163,7 +1163,7 @@ dependencies = [
[[package]]
name = "collab-user"
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 = [
"anyhow",
"collab",

View File

@ -116,13 +116,13 @@ custom-protocol = ["tauri/custom-protocol"]
# To switch to the local path, run:
# scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️
collab = { 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 = "c714b6bd458420663c7a5b29370d6892902e995c" }
collab-folder = { 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 = "c714b6bd458420663c7a5b29370d6892902e995c" }
collab-database = { 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 = "c714b6bd458420663c7a5b29370d6892902e995c" }
collab-user = { 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 = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
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 = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
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 = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
# Working directory: frontend
# To update the commit ID, run:

View File

@ -825,7 +825,7 @@ dependencies = [
[[package]]
name = "collab"
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 = [
"anyhow",
"arc-swap",
@ -850,7 +850,7 @@ dependencies = [
[[package]]
name = "collab-database"
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 = [
"anyhow",
"async-trait",
@ -879,7 +879,7 @@ dependencies = [
[[package]]
name = "collab-document"
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 = [
"anyhow",
"arc-swap",
@ -899,7 +899,7 @@ dependencies = [
[[package]]
name = "collab-entity"
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 = [
"anyhow",
"bytes",
@ -918,7 +918,7 @@ dependencies = [
[[package]]
name = "collab-folder"
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 = [
"anyhow",
"arc-swap",
@ -961,7 +961,7 @@ dependencies = [
[[package]]
name = "collab-plugins"
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 = [
"anyhow",
"async-stream",
@ -1041,7 +1041,7 @@ dependencies = [
[[package]]
name = "collab-user"
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 = [
"anyhow",
"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:
# scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️
collab = { 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 = "c714b6bd458420663c7a5b29370d6892902e995c" }
collab-folder = { 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 = "c714b6bd458420663c7a5b29370d6892902e995c" }
collab-database = { 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 = "c714b6bd458420663c7a5b29370d6892902e995c" }
collab-user = { 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 = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
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 = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
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 = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e51cde161cbce9af08f707ab1b3a3564eee90bac" }
# Working directory: frontend
# To update the commit ID, run:

View File

@ -3,14 +3,13 @@ use std::convert::TryFrom;
use bytes::Bytes;
use collab_database::database::timestamp;
use collab_database::entity::SelectOption;
use collab_database::fields::Field;
use collab_database::rows::{Row, RowId};
use flowy_database2::entities::*;
use flowy_database2::event_map::DatabaseEvent;
use flowy_database2::services::cell::CellBuilder;
use flowy_database2::services::field::{
MultiSelectTypeOption, SelectOption, SingleSelectTypeOption,
};
use flowy_database2::services::field::{MultiSelectTypeOption, SingleSelectTypeOption};
use flowy_database2::services::share::csv::CSVFormat;
use flowy_folder::entities::*;
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::entity::DatabaseView;
use collab_database::entity::{DatabaseView, SelectOption, SelectOptionColor};
use collab_database::views::DatabaseLayout;
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::translate_type_option::translate::TranslateTypeOption;
use flowy_database2::services::field::{
FieldBuilder, NumberFormat, NumberTypeOption, SelectOption, SelectOptionColor,
SingleSelectTypeOption,
FieldBuilder, NumberFormat, NumberTypeOption, SingleSelectTypeOption,
};
use flowy_database2::services::field_settings::default_field_settings_for_fields;
use strum::IntoEnumIterator;

View File

@ -245,7 +245,7 @@ async fn get_row_event_test() {
assert!(row.is_some());
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]

View File

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

View File

@ -61,3 +61,4 @@ flowy-codegen.workspace = true
[features]
dart = ["flowy-codegen/dart", "flowy-notification/dart"]
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)]
pub id: String,
#[pb(index = 2)]
pub document_id: String,
#[pb(index = 2, one_of)]
pub document_id: Option<String>,
#[pb(index = 3, one_of)]
pub icon: Option<String>,
@ -64,8 +64,8 @@ pub struct RowMetaPB {
#[pb(index = 4, one_of)]
pub cover: Option<String>,
#[pb(index = 5)]
pub is_document_empty: bool,
#[pb(index = 5, one_of)]
pub is_document_empty: Option<bool>,
}
#[derive(Debug, Default, ProtoBuf)]
@ -74,25 +74,26 @@ pub struct RepeatedRowMetaPB {
pub items: Vec<RowMetaPB>,
}
impl std::convert::From<&RowDetail> for RowMetaPB {
fn from(row_detail: &RowDetail) -> Self {
impl From<RowOrder> for RowMetaPB {
fn from(data: RowOrder) -> Self {
Self {
id: row_detail.row.id.to_string(),
document_id: row_detail.document_id.clone(),
icon: row_detail.meta.icon_url.clone(),
cover: row_detail.meta.cover_url.clone(),
is_document_empty: row_detail.meta.is_document_empty,
id: data.id.into_inner(),
document_id: None,
icon: None,
cover: None,
is_document_empty: None,
}
}
}
impl std::convert::From<RowDetail> for RowMetaPB {
fn from(row_detail: RowDetail) -> Self {
Self {
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,
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 flowy_derive::ProtoBuf;
@ -5,7 +6,6 @@ use flowy_error::{ErrorCode, FlowyError};
use crate::entities::parser::NotEmptyStr;
use crate::entities::SelectOptionPB;
use crate::services::field::SelectOption;
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct ChecklistCellDataPB {

View File

@ -1,9 +1,8 @@
use crate::entities::parser::NotEmptyStr;
use crate::entities::{CellIdPB, CellIdParams};
use crate::services::field::checklist_type_option::ChecklistTypeOption;
use crate::services::field::{
MultiSelectTypeOption, SelectOption, SelectOptionColor, SingleSelectTypeOption,
};
use crate::services::field::{MultiSelectTypeOption, SingleSelectTypeOption};
use collab_database::entity::{SelectOption, SelectOptionColor};
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
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())
.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!(
"layout: {:?}, rows: {}, fields: {}",
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())
.await?;
let database_editor = manager.get_database_editor(&database_id).await?;
let data = database_editor.get_all_rows(view_id.as_ref()).await?;
data_result_ok(data)
let row_details = database_editor
.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)]
pub(crate) async fn open_database_handler(
@ -326,7 +332,7 @@ pub(crate) async fn switch_to_field_handler(
.await?;
let old_field = database_editor.get_field(&params.field_id).await;
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?;
if let Some(new_type_option) = database_editor

View File

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

View File

@ -52,7 +52,6 @@ pub enum DatabaseNotification {
DidUpdateFieldSettings = 86,
// Trigger when Calculation changed
DidUpdateCalculation = 87,
ReloadRows = 88,
}
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::{
DatabaseViewChanged, DatabaseViewOperation, DatabaseViews, EditorByViewId,
};
use crate::services::field::type_option_transform::transform_type_option;
use crate::services::field::{
default_type_option_data_from_type, select_type_option_from_field, transform_type_option,
type_option_data_from_pb, ChecklistCellChangeset, RelationTypeOption, SelectOptionCellChangeset,
StringCellData, TimestampCellData, TimestampCellDataWrapper, TypeOptionCellDataHandler,
TypeOptionCellExt,
default_type_option_data_from_type, select_type_option_from_field, type_option_data_from_pb,
ChecklistCellChangeset, RelationTypeOption, SelectOptionCellChangeset, StringCellData,
TimestampCellData, TimestampCellDataWrapper, TypeOptionCellDataHandler, TypeOptionCellExt,
};
use crate::services::field_settings::{default_field_settings_by_layout_map, FieldSettings};
use crate::services::filter::{Filter, FilterChangeset};
@ -36,9 +36,9 @@ use lib_infra::util::timestamp;
use std::collections::HashMap;
use std::sync::Arc;
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(crate) database: Arc<RwLock<Database>>,
pub cell_cache: CellCache,
@ -48,6 +48,7 @@ pub struct DatabaseEditor {
notification_sender: Arc<DebounceNotificationSender>,
user: Arc<dyn DatabaseUser>,
collab_builder: Arc<AppFlowyCollabBuilder>,
database_cancellation: Arc<RwLock<Option<CancellationToken>>>,
}
impl DatabaseEditor {
@ -60,6 +61,7 @@ impl DatabaseEditor {
let notification_sender = Arc::new(DebounceNotificationSender::new(200));
let cell_cache = AnyTypeCache::<u64>::new();
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
observe_sync_state(&database_id, &database).await;
// observe_view_change(&database_id, &database).await;
@ -73,6 +75,7 @@ impl DatabaseEditor {
task_scheduler: task_scheduler.clone(),
cell_cache: cell_cache.clone(),
editor_by_view_id: editor_by_view_id.clone(),
database_cancellation: database_cancellation.clone(),
});
let database_views = Arc::new(
@ -104,6 +107,7 @@ impl DatabaseEditor {
database_views,
notification_sender,
collab_builder,
database_cancellation,
});
observe_block_event(&database_id, &this).await;
Ok(this)
@ -419,6 +423,7 @@ impl DatabaseEditor {
pub async fn switch_to_field_type(
&self,
view_id: &str,
field_id: &str,
new_field_type: FieldType,
) -> FlowyResult<()> {
@ -441,11 +446,16 @@ impl DatabaseEditor {
.unwrap_or_else(|| default_type_option_data_from_type(new_field_type));
let transformed_type_option = transform_type_option(
view_id,
field_id,
old_field_type,
new_field_type,
old_type_option_data,
new_type_option_data,
);
&database,
)
.await;
database.update_field(field_id, |update| {
update
.set_field_type(new_field_type.into())
@ -667,9 +677,9 @@ impl DatabaseEditor {
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?;
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> {
@ -731,10 +741,10 @@ impl DatabaseEditor {
let row_document_id = database.get_row_document_id(row_id)?;
Some(RowMetaPB {
id: row_id.clone().into_inner(),
document_id: row_document_id,
document_id: Some(row_document_id),
icon: row_meta.icon_url,
cover: row_meta.cover_url,
is_document_empty: row_meta.is_document_empty,
is_document_empty: Some(row_meta.is_document_empty),
})
} else {
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.
send_notification(row_id.as_str(), DatabaseNotification::DidUpdateRowMeta)
.payload(RowMetaPB::from(&row_detail))
.payload(RowMetaPB::from(row_detail))
.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
}
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.
/// This will notify all views that the cell has been updated.
pub async fn update_cell(
@ -919,7 +906,9 @@ impl DatabaseEditor {
.write()
.await
.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);
});
})
@ -1170,7 +1159,7 @@ impl DatabaseEditor {
let to_row = if to_row.is_some() {
to_row
} else {
let row_details = self.get_row_details(view_id).await?;
let row_details = self.get_all_row_details(view_id).await?;
row_details
.last()
.map(|row_detail| row_detail.row.id.clone())
@ -1189,7 +1178,9 @@ impl DatabaseEditor {
.write()
.await
.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;
},
@ -1296,14 +1287,34 @@ impl DatabaseEditor {
Ok(database_view_setting_pb_from_view(view))
}
pub async fn get_database_data(&self, view_id: &str) -> FlowyResult<DatabasePB> {
let database_view = self.database_views.get_view_editor(view_id).await?;
let view = database_view
.v_get_view()
.await
.ok_or_else(FlowyError::record_not_found)?;
pub async fn close_database(&self) {
let cancellation = self.database_cancellation.read().await;
if let Some(cancellation) = &*cancellation {
info!("Cancel database operation");
cancellation.cancel();
}
}
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 = self.database.read().await;
let database_id = database.get_database_id();
@ -1318,7 +1329,7 @@ impl DatabaseEditor {
let rows = row_details
.into_iter()
.map(|detail| RowMetaPB::from(detail.as_ref()))
.map(|order| RowMetaPB::from(order.as_ref().clone()))
.collect::<Vec<RowMetaPB>>();
trace!(
@ -1327,25 +1338,16 @@ impl DatabaseEditor {
fields.len(),
rows.len()
);
self.database_cancellation.write().await.take();
Ok(DatabasePB {
id: database_id,
fields,
rows,
layout_type: view.layout.into(),
layout_type: view_layout.into(),
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> {
let database = self.database.clone();
let database_guard = database.read().await;
@ -1467,6 +1469,7 @@ struct DatabaseViewOperationImpl {
task_scheduler: Arc<RwLock<TaskDispatcher>>,
cell_cache: CellCache,
editor_by_view_id: Arc<RwLock<EditorByViewId>>,
database_cancellation: Arc<RwLock<Option<CancellationToken>>>,
}
#[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 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![];
// Loading the rows in chunks of 10 rows in order to prevent blocking the main asynchronous runtime
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) {
let database_read_guard = self.database.read().await;
let chunk = chunk.to_vec();
let rows = database_read_guard.get_rows_from_row_orders(&chunk).await;
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 {
None => warn!("Failed to get row detail for row: {}", row.id.as_str()),
Some(row_details) => {

View File

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

View File

@ -313,8 +313,8 @@ impl DatabaseViewEditor {
}
#[instrument(level = "info", skip(self))]
pub async fn v_get_row_details(&self) -> Vec<Arc<RowDetail>> {
let mut rows = self.delegate.get_row_details(&self.view_id).await;
pub async fn v_get_all_row_details(&self) -> Vec<Arc<RowDetail>> {
let mut rows = self.delegate.get_all_row_details(&self.view_id).await;
self.v_filter_rows(&mut rows).await;
self.v_sort_rows(&mut rows).await;
rows
@ -937,7 +937,7 @@ impl DatabaseViewEditor {
let (_, row_detail) = self.delegate.get_row_detail(&self.view_id, &row_id).await?;
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(),
title,
timestamp,
@ -1000,7 +1000,7 @@ impl DatabaseViewEditor {
let (_, row_detail) = self.delegate.get_row_detail(&self.view_id, &row_id).await?;
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(),
title,
timestamp,

View File

@ -53,7 +53,7 @@ impl FilterDelegate for DatabaseViewFilterDelegateImpl {
}
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>)> {

View File

@ -97,7 +97,7 @@ impl GroupControllerDelegate for GroupControllerDelegateImpl {
}
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;
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>)>;
/// 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>;

View File

@ -61,7 +61,7 @@ impl SortDelegate for DatabaseViewSortDelegateImpl {
async fn get_rows(&self, view_id: &str) -> Vec<Arc<RowDetail>> {
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;
row_details
}

View File

@ -1,5 +1,6 @@
mod field_builder;
mod field_operation;
pub(crate) mod type_option_transform;
pub mod type_options;
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::rows::Cell;
use flowy_error::FlowyResult;
use std::cmp::Ordering;
use crate::entities::{ChecklistCellDataPB, ChecklistFilterPB, SelectOptionPB};
use crate::services::cell::{CellDataChangeset, CellDataDecoder};
use crate::services::field::checklist_type_option::{ChecklistCellChangeset, ChecklistCellData};
use crate::services::field::{
SelectOption, TypeOption, TypeOptionCellData, TypeOptionCellDataCompare,
TypeOptionCellDataFilter, TypeOptionCellDataSerde, TypeOptionTransform, SELECTION_IDS_SEPARATOR,
TypeOption, TypeOptionCellData, TypeOptionCellDataCompare, TypeOptionCellDataFilter,
TypeOptionCellDataSerde, TypeOptionTransform, SELECTION_IDS_SEPARATOR,
};
use crate::services::sort::SortCondition;

View File

@ -1,6 +1,7 @@
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_database::entity::SelectOption;
use collab_database::rows::{new_cell_builder, Cell};
use serde::{Deserialize, Serialize};
use std::fmt::Debug;

View File

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

View File

@ -1,18 +1,17 @@
use collab::util::AnyMapExt;
use std::cmp::Ordering;
use collab_database::entity::SelectOption;
use collab_database::fields::{TypeOptionData, TypeOptionDataBuilder};
use collab_database::rows::Cell;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use flowy_error::FlowyResult;
use crate::entities::{FieldType, SelectOptionCellDataPB, SelectOptionFilterPB};
use crate::services::cell::CellDataChangeset;
use crate::services::field::{
default_order, SelectOption, SelectOptionCellChangeset, SelectOptionIds,
SelectTypeOptionSharedAction, TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter,
TypeOptionCellDataSerde,
default_order, SelectOptionCellChangeset, SelectOptionIds, SelectTypeOptionSharedAction,
TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, TypeOptionCellDataSerde,
};
use crate::services::sort::SortCondition;
@ -176,39 +175,10 @@ impl TypeOptionCellDataCompare for MultiSelectTypeOption {
#[cfg(test)]
mod tests {
use crate::entities::FieldType;
use crate::services::cell::CellDataChangeset;
use crate::services::field::type_options::selection_type_option::*;
use crate::services::field::MultiSelectTypeOption;
use crate::services::field::{CheckboxTypeOption, TypeOptionTransform};
#[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);
}
use collab_database::entity::SelectOption;
#[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::rows::Cell;
use crate::entities::{SelectOptionFilterConditionPB, SelectOptionFilterPB};
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;
impl SelectOptionFilterPB {
@ -132,7 +133,7 @@ impl PreFillCellsWithFilter for SelectOptionFilterPB {
#[cfg(test)]
mod tests {
use crate::entities::{SelectOptionFilterConditionPB, SelectOptionFilterPB};
use crate::services::field::SelectOption;
use collab_database::entity::SelectOption;
#[test]
fn select_option_filter_is_empty_test() {

View File

@ -1,50 +1,6 @@
use crate::entities::SelectOptionCellDataPB;
use crate::services::field::SelectOptionIds;
use collab_database::database::gen_option_id;
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,
}
use collab_database::entity::SelectOption;
#[derive(Debug)]
pub struct SelectOptionCellData {
pub select_options: Vec<SelectOption>,

View File

@ -1,18 +1,19 @@
use std::str::FromStr;
use async_trait::async_trait;
use bytes::Bytes;
use collab_database::database::Database;
use collab_database::entity::{SelectOption, SelectOptionColor};
use collab_database::fields::{Field, TypeOptionData};
use collab_database::rows::Cell;
use flowy_error::{internal_error, ErrorCode, FlowyResult};
use std::str::FromStr;
use crate::entities::{CheckboxCellDataPB, FieldType, SelectOptionCellDataPB};
use crate::services::cell::{CellDataDecoder, CellProtobufBlobParser};
use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformHelper;
use crate::services::field::{
make_selected_options, MultiSelectTypeOption, SelectOption, SelectOptionCellData,
SelectOptionColor, SelectOptionIds, SingleSelectTypeOption, TypeOption, TypeOptionCellDataSerde,
TypeOptionTransform, SELECTION_IDS_SEPARATOR,
make_selected_options, MultiSelectTypeOption, SelectOptionCellData, SelectOptionIds,
SingleSelectTypeOption, StringCellData, TypeOption, TypeOptionCellDataSerde, TypeOptionTransform,
SELECTION_IDS_SEPARATOR,
};
/// 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>;
}
#[async_trait]
impl<T> TypeOptionTransform for T
where
T: SelectTypeOptionSharedAction + TypeOption<CellData = SelectOptionIds> + CellDataDecoder,
{
fn transform_type_option(
async fn transform_type_option(
&mut self,
_old_type_option_field_type: FieldType,
_old_type_option_data: TypeOptionData,
view_id: &str,
field_id: &str,
old_type_option_field_type: FieldType,
old_type_option_data: TypeOptionData,
database: &Database,
) {
SelectOptionTypeOptionTransformHelper::transform_type_option(
self,
&_old_type_option_field_type,
_old_type_option_data,
);
view_id,
field_id,
&old_type_option_field_type,
old_type_option_data,
database,
)
.await;
}
}
@ -101,6 +110,15 @@ where
_field: &Field,
) -> Option<<Self as TypeOption>::CellData> {
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 => {
let cell_content = CheckboxCellDataPB::from(cell).to_string();
let mut transformed_ids = Vec::new();
@ -110,7 +128,6 @@ where
}
Some(SelectOptionIds::from(transformed_ids))
},
FieldType::RichText => Some(SelectOptionIds::from(cell)),
FieldType::SingleSelect | FieldType::MultiSelect => Some(SelectOptionIds::from(cell)),
_ => None,
}

View File

@ -1,7 +1,7 @@
use crate::entities::{FieldType, SelectOptionCellDataPB, SelectOptionFilterPB};
use crate::services::cell::CellDataChangeset;
use crate::services::field::{
default_order, SelectOption, TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter,
default_order, TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter,
TypeOptionCellDataSerde,
};
use crate::services::field::{
@ -9,6 +9,7 @@ use crate::services::field::{
};
use crate::services::sort::SortCondition;
use collab::util::AnyMapExt;
use collab_database::entity::SelectOption;
use collab_database::fields::{TypeOptionData, TypeOptionDataBuilder};
use collab_database::rows::Cell;
use flowy_error::FlowyResult;
@ -149,40 +150,9 @@ impl TypeOptionCellDataCompare for SingleSelectTypeOption {
#[cfg(test)]
mod tests {
use crate::entities::FieldType;
use crate::services::cell::CellDataChangeset;
use crate::services::field::type_options::*;
#[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);
}
use collab_database::entity::SelectOption;
#[test]
fn single_select_insert_multi_option_test() {

View File

@ -1,9 +1,13 @@
use crate::entities::FieldType;
use crate::services::cell::CellDataDecoder;
use crate::services::field::{
MultiSelectTypeOption, SelectOption, SelectOptionColor, SelectOptionIds,
SelectTypeOptionSharedAction, SingleSelectTypeOption, TypeOption, CHECK, UNCHECK,
MultiSelectTypeOption, RichTextTypeOption, SelectOptionIds, SelectTypeOptionSharedAction,
SingleSelectTypeOption, TypeOption, CHECK, UNCHECK,
};
use collab_database::database::Database;
use collab_database::entity::{SelectOption, SelectOptionColor};
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
pub(crate) struct SelectOptionTypeOptionTransformHelper();
@ -14,14 +18,37 @@ impl SelectOptionTypeOptionTransformHelper {
///
/// * `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,
view_id: &str,
field_id: &str,
old_field_type: &FieldType,
old_type_option_data: TypeOptionData,
database: &Database,
) where
T: SelectTypeOptionSharedAction + TypeOption<CellData = SelectOptionIds>,
{
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 => {
// add Yes and No options if it does not exist.
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::field::FieldBuilder;
use crate::services::field::*;
use collab_database::entity::SelectOption;
// Test parser the cell data which field's type is FieldType::Date to cell data
// which field's type is FieldType::Text

View File

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

View File

@ -1,10 +1,11 @@
use std::cmp::Ordering;
use std::fmt::Debug;
use async_trait::async_trait;
use bytes::Bytes;
use collab_database::database::Database;
use collab_database::fields::TypeOptionData;
use collab_database::rows::Cell;
use protobuf::ProtobufError;
use std::cmp::Ordering;
use std::fmt::Debug;
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
/// 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.
@ -104,10 +106,13 @@ pub trait TypeOptionTransform: 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`.
///
fn transform_type_option(
async fn transform_type_option(
&mut self,
_view_id: &str,
_field_id: &str,
_old_type_option_field_type: FieldType,
_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::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 flowy_error::FlowyResult;
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;
}
#[derive(Debug)]
struct CellDataCacheKey(u64);
impl CellDataCacheKey {
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> {
let key = self.get_cell_data_cache_key(cell, field);
let cell_data_cache = self.cell_data_cache.as_ref()?;
let cell = cell_data_cache.get::<T::CellData>(key.as_ref())?;
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(
from_field_type: FieldType,
to_field_type: FieldType,
) -> bool {
matches!(
(from_field_type, to_field_type),
// Checkbox
(FieldType::Checkbox, FieldType::SingleSelect)
| (FieldType::Checkbox, FieldType::MultiSelect)
// SingleSelect or MultiSelect
| (FieldType::SingleSelect, FieldType::MultiSelect)
| (FieldType::MultiSelect, FieldType::SingleSelect)
// Text
| (FieldType::RichText, FieldType::SingleSelect)
| (FieldType::RichText, FieldType::MultiSelect)
| (FieldType::RichText, FieldType::URL)
| (_, 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 struct RowSingleCellData {

View File

@ -1,13 +1,13 @@
use collab::preclude::encoding::serde::from_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 flowy_error::FlowyResult;
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::field::{
TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, TypeOptionCellDataSerde,
@ -64,6 +64,17 @@ impl CellDataDecoder for URLTypeOption {
fn decode_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
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 {
cell_data.data

View File

@ -360,7 +360,8 @@ impl FilterController {
if is_visible {
if let Some((index, _row)) = self.delegate.get_row(&self.view_id, &row_id).await {
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 {
@ -397,7 +398,7 @@ impl FilterController {
&filters,
) {
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))
} else {
invisible_rows.push(row_detail.row.id.clone());

View File

@ -115,7 +115,7 @@ where
if !no_status_group_rows.is_empty() {
changeset
.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());
}

View File

@ -68,7 +68,7 @@ impl GroupCustomize for CheckboxGroupController {
if is_not_contained {
changeset
.inserted_rows
.push(InsertedRowPB::new(RowMetaPB::from(row_detail)));
.push(InsertedRowPB::new(RowMetaPB::from(row_detail.clone())));
group.add_row(row_detail.clone());
}
}
@ -86,7 +86,7 @@ impl GroupCustomize for CheckboxGroupController {
if is_not_contained {
changeset
.inserted_rows
.push(InsertedRowPB::new(RowMetaPB::from(row_detail)));
.push(InsertedRowPB::new(RowMetaPB::from(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 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);
}
@ -130,7 +133,7 @@ impl GroupCustomize for DateGroupController {
if !group.contains_row(&row_detail.row.id) {
changeset
.inserted_rows
.push(InsertedRowPB::new(RowMetaPB::from(row_detail)));
.push(InsertedRowPB::new(RowMetaPB::from(row_detail.clone())));
group.add_row(row_detail.clone());
}
} else if group.contains_row(&row_detail.row.id) {

View File

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

View File

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

View File

@ -1,4 +1,5 @@
use chrono::NaiveDateTime;
use collab_database::entity::SelectOption;
use collab_database::fields::Field;
use collab_database::rows::{Cell, Row, RowDetail};
@ -8,7 +9,7 @@ use crate::entities::{
use crate::services::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};
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) {
changeset
.inserted_rows
.push(InsertedRowPB::new(RowMetaPB::from(row_detail)));
.push(InsertedRowPB::new(RowMetaPB::from(row_detail.clone())));
group.add_row(row_detail.clone());
}
} 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 group = Group::new(cell_data.data);
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);
}
@ -98,7 +101,7 @@ impl GroupCustomize for URLGroupController {
if !group.contains_row(&row_detail.row.id) {
changeset
.inserted_rows
.push(InsertedRowPB::new(RowMetaPB::from(row_detail)));
.push(InsertedRowPB::new(RowMetaPB::from(row_detail.clone())));
group.add_row(row_detail.clone());
}
} 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::entity::{CreateDatabaseParams, CreateViewParams};
use collab_database::entity::{
CreateDatabaseParams, CreateViewParams, SelectOption, SelectOptionColor,
};
use collab_database::rows::CreateRowParams;
use collab_database::views::{DatabaseLayout, LayoutSettings};
use crate::entities::FieldType;
use crate::services::cell::{insert_select_option_cell, insert_text_cell};
use crate::services::field::{
FieldBuilder, SelectOption, SelectOptionColor, SingleSelectTypeOption,
};
use crate::services::field::{FieldBuilder, SingleSelectTypeOption};
use crate::services::field_settings::default_field_settings_for_fields;
use crate::services::setting::{BoardLayoutSetting, CalendarLayoutSetting};

View File

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

View File

@ -16,6 +16,7 @@ pub enum FieldScript {
field: Field,
},
SwitchToField {
view_id: String,
field_id: String,
new_field_type: FieldType,
},
@ -80,13 +81,14 @@ impl DatabaseFieldTest {
assert_eq!(self.field_count, fields.len());
},
FieldScript::SwitchToField {
view_id,
field_id,
new_field_type,
} => {
//
self
.editor
.switch_to_field_type(&field_id, new_field_type)
.switch_to_field_type(&view_id, &field_id, new_field_type)
.await
.unwrap();
},
@ -121,7 +123,11 @@ impl DatabaseFieldTest {
} => {
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 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::entity::SelectOption;
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::FieldScript::*;
@ -122,6 +122,7 @@ async fn grid_delete_field() {
async fn grid_switch_from_select_option_to_checkbox_test() {
let mut test = DatabaseFieldTest::new().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
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(),
},
SwitchToField {
view_id: view_id.clone(),
field_id: field.id.clone(),
new_field_type: FieldType::Checkbox,
},
@ -163,6 +165,7 @@ async fn grid_switch_from_checkbox_to_select_option_test() {
let scripts = vec![
// switch to single-select field type
SwitchToField {
view_id: test.view_id(),
field_id: checkbox_field.id.clone(),
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 script_switch_field = vec![SwitchToField {
view_id: test.view_id(),
field_id: field_rev.id.clone(),
new_field_type: FieldType::RichText,
}];
@ -229,6 +233,7 @@ async fn grid_switch_from_checkbox_to_text_test() {
let scripts = vec![
SwitchToField {
view_id: test.view_id(),
field_id: field_rev.id.clone(),
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 scripts = vec![
SwitchToField {
view_id: test.view_id(),
field_id: field.id.clone(),
new_field_type: FieldType::RichText,
},
@ -282,6 +288,7 @@ async fn grid_switch_from_number_to_text_test() {
let scripts = vec![
SwitchToField {
view_id: test.view_id(),
field_id: field.id.clone(),
new_field_type: FieldType::RichText,
},
@ -308,6 +315,7 @@ async fn grid_switch_from_checklist_to_text_test() {
let scripts = vec![
SwitchToField {
view_id: test.view_id(),
field_id: field_rev.id.clone(),
new_field_type: FieldType::RichText,
},

View File

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

View File

@ -301,7 +301,7 @@ impl DatabaseFilterTest {
}
},
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);
},
FilterScript::Wait { millisecond } => {

View File

@ -1,3 +1,4 @@
use collab_database::entity::SelectOption;
use collab_database::fields::Field;
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,
};
use flowy_database2::services::field::{
edit_single_select_type_option, SelectOption, SelectTypeOptionSharedAction,
SingleSelectTypeOption,
edit_single_select_type_option, SelectTypeOptionSharedAction, SingleSelectTypeOption,
};
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::GroupScript::*;
use collab_database::entity::SelectOption;
#[tokio::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::entity::DatabaseView;
use collab_database::entity::{DatabaseView, SelectOption, SelectOptionColor};
use collab_database::views::{DatabaseLayout, LayoutSetting, LayoutSettings};
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::{
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::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::entity::DatabaseView;
use collab_database::entity::{DatabaseView, SelectOption, SelectOptionColor};
use collab_database::views::DatabaseLayout;
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::{
ChecklistTypeOption, DateFormat, DateTypeOption, FieldBuilder, MultiSelectTypeOption,
NumberFormat, NumberTypeOption, RelationTypeOption, SelectOption, SelectOptionColor,
SingleSelectTypeOption, TimeFormat, TimeTypeOption, TimestampTypeOption,
NumberFormat, NumberTypeOption, RelationTypeOption, SingleSelectTypeOption, TimeFormat,
TimeTypeOption, TimestampTypeOption,
};
use flowy_database2::services::field_settings::default_field_settings_for_fields;

View File

@ -86,7 +86,11 @@ impl DatabasePreFillRowCellTest {
.await
.unwrap(),
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());
},
PreFillRowCellTestScript::AssertCellExistence {
@ -94,7 +98,11 @@ impl DatabasePreFillRowCellTest {
row_index,
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 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 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 cell = row_detail
@ -125,7 +137,11 @@ impl DatabasePreFillRowCellTest {
row_index,
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 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 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[1].field_type, 1);
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 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[1].field_type, 1);
assert_eq!(fields[2].field_type, 2);

View File

@ -117,7 +117,11 @@ impl DatabaseSortTest {
},
SortScript::AssertCellContentOrder { field_id, orders } => {
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();
for row_detail in rows {
if let Some(cell) = row_detail.row.cells.get(&field_id) {