mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: some bugs (#2639)
* fix: invalid index when insert row * fix: auto fill 0 in front of number start with . * fix: #2591 * chore: split the update at and create at test * chore: fix tauri build
This commit is contained in:
@ -83,13 +83,13 @@ class RowCache {
|
|||||||
await _cellCache.dispose();
|
await _cellCache.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void applyRowsChanged(RowsChangesetPB changeset) {
|
void applyRowsChanged(RowsChangePB changeset) {
|
||||||
_deleteRows(changeset.deletedRows);
|
_deleteRows(changeset.deletedRows);
|
||||||
_insertRows(changeset.insertedRows);
|
_insertRows(changeset.insertedRows);
|
||||||
_updateRows(changeset.updatedRows);
|
_updateRows(changeset.updatedRows);
|
||||||
}
|
}
|
||||||
|
|
||||||
void applyRowsVisibility(RowsVisibilityChangesetPB changeset) {
|
void applyRowsVisibility(RowsVisibilityChangePB changeset) {
|
||||||
_hideRows(changeset.invisibleRows);
|
_hideRows(changeset.invisibleRows);
|
||||||
_showRows(changeset.visibleRows);
|
_showRows(changeset.visibleRows);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ typedef OnRowChanged = void Function(CellByFieldId, RowsChangedReason);
|
|||||||
|
|
||||||
class RowController {
|
class RowController {
|
||||||
final RowId rowId;
|
final RowId rowId;
|
||||||
|
final String? groupId;
|
||||||
final String viewId;
|
final String viewId;
|
||||||
final List<VoidCallback> _onRowChangedListeners = [];
|
final List<VoidCallback> _onRowChangedListeners = [];
|
||||||
final RowCache _rowCache;
|
final RowCache _rowCache;
|
||||||
@ -17,6 +18,7 @@ class RowController {
|
|||||||
required this.rowId,
|
required this.rowId,
|
||||||
required this.viewId,
|
required this.viewId,
|
||||||
required RowCache rowCache,
|
required RowCache rowCache,
|
||||||
|
this.groupId,
|
||||||
}) : _rowCache = rowCache;
|
}) : _rowCache = rowCache;
|
||||||
|
|
||||||
CellByFieldId loadData() {
|
CellByFieldId loadData() {
|
||||||
|
@ -36,10 +36,16 @@ class RowBackendService {
|
|||||||
return DatabaseEventDeleteRow(payload).send();
|
return DatabaseEventDeleteRow(payload).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Either<Unit, FlowyError>> duplicateRow(RowId rowId) {
|
Future<Either<Unit, FlowyError>> duplicateRow({
|
||||||
|
required RowId rowId,
|
||||||
|
String? groupId,
|
||||||
|
}) {
|
||||||
final payload = RowIdPB.create()
|
final payload = RowIdPB.create()
|
||||||
..viewId = viewId
|
..viewId = viewId
|
||||||
..rowId = rowId;
|
..rowId = rowId;
|
||||||
|
if (groupId != null) {
|
||||||
|
payload.groupId = groupId;
|
||||||
|
}
|
||||||
|
|
||||||
return DatabaseEventDuplicateRow(payload).send();
|
return DatabaseEventDuplicateRow(payload).send();
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,9 @@ import 'package:appflowy_backend/protobuf/flowy-database2/notification.pb.dart';
|
|||||||
import 'package:appflowy_backend/protobuf/flowy-database2/view_entities.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-database2/view_entities.pb.dart';
|
||||||
|
|
||||||
typedef RowsVisibilityNotifierValue
|
typedef RowsVisibilityNotifierValue
|
||||||
= Either<RowsVisibilityChangesetPB, FlowyError>;
|
= Either<RowsVisibilityChangePB, FlowyError>;
|
||||||
|
|
||||||
typedef NumberOfRowsNotifierValue = Either<RowsChangesetPB, FlowyError>;
|
typedef NumberOfRowsNotifierValue = Either<RowsChangePB, FlowyError>;
|
||||||
typedef ReorderAllRowsNotifierValue = Either<List<String>, FlowyError>;
|
typedef ReorderAllRowsNotifierValue = Either<List<String>, FlowyError>;
|
||||||
typedef SingleRowNotifierValue = Either<ReorderSingleRowPB, FlowyError>;
|
typedef SingleRowNotifierValue = Either<ReorderSingleRowPB, FlowyError>;
|
||||||
|
|
||||||
@ -54,14 +54,14 @@ class DatabaseViewListener {
|
|||||||
case DatabaseNotification.DidUpdateViewRowsVisibility:
|
case DatabaseNotification.DidUpdateViewRowsVisibility:
|
||||||
result.fold(
|
result.fold(
|
||||||
(payload) => _rowsVisibility?.value =
|
(payload) => _rowsVisibility?.value =
|
||||||
left(RowsVisibilityChangesetPB.fromBuffer(payload)),
|
left(RowsVisibilityChangePB.fromBuffer(payload)),
|
||||||
(error) => _rowsVisibility?.value = right(error),
|
(error) => _rowsVisibility?.value = right(error),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case DatabaseNotification.DidUpdateViewRows:
|
case DatabaseNotification.DidUpdateViewRows:
|
||||||
result.fold(
|
result.fold(
|
||||||
(payload) =>
|
(payload) =>
|
||||||
_rowsNotifier?.value = left(RowsChangesetPB.fromBuffer(payload)),
|
_rowsNotifier?.value = left(RowsChangePB.fromBuffer(payload)),
|
||||||
(error) => _rowsNotifier?.value = right(error),
|
(error) => _rowsNotifier?.value = right(error),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -265,6 +265,7 @@ class _BoardContentState extends State<BoardContent> {
|
|||||||
renderHook: renderHook,
|
renderHook: renderHook,
|
||||||
openCard: (context) => _openCard(
|
openCard: (context) => _openCard(
|
||||||
viewId,
|
viewId,
|
||||||
|
groupData.group.groupId,
|
||||||
fieldController,
|
fieldController,
|
||||||
rowPB,
|
rowPB,
|
||||||
rowCache,
|
rowCache,
|
||||||
@ -302,6 +303,7 @@ class _BoardContentState extends State<BoardContent> {
|
|||||||
|
|
||||||
void _openCard(
|
void _openCard(
|
||||||
String viewId,
|
String viewId,
|
||||||
|
String groupId,
|
||||||
FieldController fieldController,
|
FieldController fieldController,
|
||||||
RowPB rowPB,
|
RowPB rowPB,
|
||||||
RowCache rowCache,
|
RowCache rowCache,
|
||||||
@ -317,6 +319,7 @@ class _BoardContentState extends State<BoardContent> {
|
|||||||
rowId: rowInfo.rowPB.id,
|
rowId: rowInfo.rowPB.id,
|
||||||
viewId: rowInfo.viewId,
|
viewId: rowInfo.viewId,
|
||||||
rowCache: rowCache,
|
rowCache: rowCache,
|
||||||
|
groupId: groupId,
|
||||||
);
|
);
|
||||||
|
|
||||||
FlowyOverlay.show(
|
FlowyOverlay.show(
|
||||||
@ -324,7 +327,7 @@ class _BoardContentState extends State<BoardContent> {
|
|||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return RowDetailPage(
|
return RowDetailPage(
|
||||||
cellBuilder: GridCellBuilder(cellCache: dataController.cellCache),
|
cellBuilder: GridCellBuilder(cellCache: dataController.cellCache),
|
||||||
dataController: dataController,
|
rowController: dataController,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -241,7 +241,7 @@ void showEventDetails({
|
|||||||
cellBuilder: GridCellBuilder(
|
cellBuilder: GridCellBuilder(
|
||||||
cellCache: rowCache.cellCache,
|
cellCache: rowCache.cellCache,
|
||||||
),
|
),
|
||||||
dataController: dataController,
|
rowController: dataController,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -18,14 +18,14 @@ class RowActionSheetBloc
|
|||||||
super(RowActionSheetState.initial(rowInfo)) {
|
super(RowActionSheetState.initial(rowInfo)) {
|
||||||
on<RowActionSheetEvent>(
|
on<RowActionSheetEvent>(
|
||||||
(event, emit) async {
|
(event, emit) async {
|
||||||
await event.map(
|
await event.when(
|
||||||
deleteRow: (_DeleteRow value) async {
|
deleteRow: () async {
|
||||||
final result = await _rowService.deleteRow(state.rowData.rowPB.id);
|
final result = await _rowService.deleteRow(state.rowData.rowPB.id);
|
||||||
logResult(result);
|
logResult(result);
|
||||||
},
|
},
|
||||||
duplicateRow: (_DuplicateRow value) async {
|
duplicateRow: () async {
|
||||||
final result =
|
final result =
|
||||||
await _rowService.duplicateRow(state.rowData.rowPB.id);
|
await _rowService.duplicateRow(rowId: state.rowData.rowPB.id);
|
||||||
logResult(result);
|
logResult(result);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -38,8 +38,11 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
|
|||||||
deleteRow: (rowId) async {
|
deleteRow: (rowId) async {
|
||||||
await rowService.deleteRow(rowId);
|
await rowService.deleteRow(rowId);
|
||||||
},
|
},
|
||||||
duplicateRow: (String rowId) async {
|
duplicateRow: (String rowId, String? groupId) async {
|
||||||
await rowService.duplicateRow(rowId);
|
await rowService.duplicateRow(
|
||||||
|
rowId: rowId,
|
||||||
|
groupId: groupId,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -68,7 +71,8 @@ class RowDetailEvent with _$RowDetailEvent {
|
|||||||
const factory RowDetailEvent.initial() = _Initial;
|
const factory RowDetailEvent.initial() = _Initial;
|
||||||
const factory RowDetailEvent.deleteField(String fieldId) = _DeleteField;
|
const factory RowDetailEvent.deleteField(String fieldId) = _DeleteField;
|
||||||
const factory RowDetailEvent.deleteRow(String rowId) = _DeleteRow;
|
const factory RowDetailEvent.deleteRow(String rowId) = _DeleteRow;
|
||||||
const factory RowDetailEvent.duplicateRow(String rowId) = _DuplicateRow;
|
const factory RowDetailEvent.duplicateRow(String rowId, String? groupId) =
|
||||||
|
_DuplicateRow;
|
||||||
const factory RowDetailEvent.didReceiveCellDatas(
|
const factory RowDetailEvent.didReceiveCellDatas(
|
||||||
List<CellIdentifier> gridCells,
|
List<CellIdentifier> gridCells,
|
||||||
) = _DidReceiveCellDatas;
|
) = _DidReceiveCellDatas;
|
||||||
|
@ -348,7 +348,7 @@ class _GridRowsState extends State<_GridRows> {
|
|||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return RowDetailPage(
|
return RowDetailPage(
|
||||||
cellBuilder: cellBuilder,
|
cellBuilder: cellBuilder,
|
||||||
dataController: dataController,
|
rowController: dataController,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -26,11 +26,11 @@ import '../../grid/presentation/widgets/header/field_cell.dart';
|
|||||||
import '../../grid/presentation/widgets/header/field_editor.dart';
|
import '../../grid/presentation/widgets/header/field_editor.dart';
|
||||||
|
|
||||||
class RowDetailPage extends StatefulWidget with FlowyOverlayDelegate {
|
class RowDetailPage extends StatefulWidget with FlowyOverlayDelegate {
|
||||||
final RowController dataController;
|
final RowController rowController;
|
||||||
final GridCellBuilder cellBuilder;
|
final GridCellBuilder cellBuilder;
|
||||||
|
|
||||||
const RowDetailPage({
|
const RowDetailPage({
|
||||||
required this.dataController,
|
required this.rowController,
|
||||||
required this.cellBuilder,
|
required this.cellBuilder,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
@ -49,7 +49,7 @@ class _RowDetailPageState extends State<RowDetailPage> {
|
|||||||
return FlowyDialog(
|
return FlowyDialog(
|
||||||
child: BlocProvider(
|
child: BlocProvider(
|
||||||
create: (context) {
|
create: (context) {
|
||||||
return RowDetailBloc(dataController: widget.dataController)
|
return RowDetailBloc(dataController: widget.rowController)
|
||||||
..add(const RowDetailEvent.initial());
|
..add(const RowDetailEvent.initial());
|
||||||
},
|
},
|
||||||
child: ListView(
|
child: ListView(
|
||||||
@ -69,11 +69,11 @@ class _RowDetailPageState extends State<RowDetailPage> {
|
|||||||
Widget _responsiveRowInfo() {
|
Widget _responsiveRowInfo() {
|
||||||
final rowDataColumn = _PropertyColumn(
|
final rowDataColumn = _PropertyColumn(
|
||||||
cellBuilder: widget.cellBuilder,
|
cellBuilder: widget.cellBuilder,
|
||||||
viewId: widget.dataController.viewId,
|
viewId: widget.rowController.viewId,
|
||||||
);
|
);
|
||||||
final rowOptionColumn = _RowOptionColumn(
|
final rowOptionColumn = _RowOptionColumn(
|
||||||
viewId: widget.dataController.viewId,
|
viewId: widget.rowController.viewId,
|
||||||
rowId: widget.dataController.rowId,
|
rowController: widget.rowController,
|
||||||
);
|
);
|
||||||
if (MediaQuery.of(context).size.width > 800) {
|
if (MediaQuery.of(context).size.width > 800) {
|
||||||
return Row(
|
return Row(
|
||||||
@ -372,10 +372,10 @@ GridCellStyle? _customCellStyle(FieldType fieldType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _RowOptionColumn extends StatelessWidget {
|
class _RowOptionColumn extends StatelessWidget {
|
||||||
final String rowId;
|
final RowController rowController;
|
||||||
const _RowOptionColumn({
|
const _RowOptionColumn({
|
||||||
required String viewId,
|
required String viewId,
|
||||||
required this.rowId,
|
required this.rowController,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@ -390,8 +390,11 @@ class _RowOptionColumn extends StatelessWidget {
|
|||||||
child: FlowyText(LocaleKeys.grid_row_action.tr()),
|
child: FlowyText(LocaleKeys.grid_row_action.tr()),
|
||||||
),
|
),
|
||||||
const VSpace(15),
|
const VSpace(15),
|
||||||
_DeleteButton(rowId: rowId),
|
_DeleteButton(rowId: rowController.rowId),
|
||||||
_DuplicateButton(rowId: rowId),
|
_DuplicateButton(
|
||||||
|
rowId: rowController.rowId,
|
||||||
|
groupId: rowController.groupId,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -419,7 +422,12 @@ class _DeleteButton extends StatelessWidget {
|
|||||||
|
|
||||||
class _DuplicateButton extends StatelessWidget {
|
class _DuplicateButton extends StatelessWidget {
|
||||||
final String rowId;
|
final String rowId;
|
||||||
const _DuplicateButton({required this.rowId, Key? key}) : super(key: key);
|
final String? groupId;
|
||||||
|
const _DuplicateButton({
|
||||||
|
required this.rowId,
|
||||||
|
this.groupId,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -429,7 +437,9 @@ class _DuplicateButton extends StatelessWidget {
|
|||||||
text: FlowyText.regular(LocaleKeys.grid_row_duplicate.tr()),
|
text: FlowyText.regular(LocaleKeys.grid_row_duplicate.tr()),
|
||||||
leftIcon: const FlowySvg(name: "grid/duplicate"),
|
leftIcon: const FlowySvg(name: "grid/duplicate"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.read<RowDetailBloc>().add(RowDetailEvent.duplicateRow(rowId));
|
context
|
||||||
|
.read<RowDetailBloc>()
|
||||||
|
.add(RowDetailEvent.duplicateRow(rowId, groupId));
|
||||||
FlowyOverlay.pop(context);
|
FlowyOverlay.pop(context);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
20
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
20
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -99,7 +99,7 @@ checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "appflowy-integrate"
|
name = "appflowy-integrate"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -1023,7 +1023,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab"
|
name = "collab"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -1040,7 +1040,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-client-ws"
|
name = "collab-client-ws"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"collab-sync",
|
"collab-sync",
|
||||||
@ -1058,7 +1058,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-database"
|
name = "collab-database"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1083,7 +1083,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-derive"
|
name = "collab-derive"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1095,7 +1095,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-document"
|
name = "collab-document"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -1112,7 +1112,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-folder"
|
name = "collab-folder"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -1130,7 +1130,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-persistence"
|
name = "collab-persistence"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -1150,7 +1150,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-plugins"
|
name = "collab-plugins"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1180,7 +1180,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-sync"
|
name = "collab-sync"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"collab",
|
"collab",
|
||||||
|
@ -34,12 +34,12 @@ default = ["custom-protocol"]
|
|||||||
custom-protocol = ["tauri/custom-protocol"]
|
custom-protocol = ["tauri/custom-protocol"]
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d6af3a" }
|
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "173661" }
|
||||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d6af3a" }
|
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "173661" }
|
||||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d6af3a" }
|
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "173661" }
|
||||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d6af3a" }
|
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "173661" }
|
||||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d6af3a" }
|
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "173661" }
|
||||||
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d6af3a" }
|
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "173661" }
|
||||||
|
|
||||||
#collab = { path = "../../AppFlowy-Collab/collab" }
|
#collab = { path = "../../AppFlowy-Collab/collab" }
|
||||||
#collab-folder = { path = "../../AppFlowy-Collab/collab-folder" }
|
#collab-folder = { path = "../../AppFlowy-Collab/collab-folder" }
|
||||||
|
@ -4,17 +4,17 @@ import {
|
|||||||
UpdatedRowPB,
|
UpdatedRowPB,
|
||||||
RowIdPB,
|
RowIdPB,
|
||||||
OptionalRowPB,
|
OptionalRowPB,
|
||||||
RowsChangesetPB,
|
RowsChangePB,
|
||||||
RowsVisibilityChangesetPB,
|
RowsVisibilityChangePB,
|
||||||
ReorderSingleRowPB
|
ReorderSingleRowPB,
|
||||||
} from "@/services/backend";
|
} from '@/services/backend';
|
||||||
import { ChangeNotifier } from "$app/utils/change_notifier";
|
import { ChangeNotifier } from '$app/utils/change_notifier';
|
||||||
import { FieldInfo } from "../field/field_controller";
|
import { FieldInfo } from '../field/field_controller';
|
||||||
import { CellCache, CellCacheKey } from "../cell/cell_cache";
|
import { CellCache, CellCacheKey } from '../cell/cell_cache';
|
||||||
import { CellIdentifier } from "../cell/cell_bd_svc";
|
import { CellIdentifier } from '../cell/cell_bd_svc';
|
||||||
import { DatabaseEventGetRow } from "@/services/backend/events/flowy-database2";
|
import { DatabaseEventGetRow } from '@/services/backend/events/flowy-database2';
|
||||||
import { None, Option, Some } from "ts-results";
|
import { None, Option, Some } from 'ts-results';
|
||||||
import { Log } from "$app/utils/log";
|
import { Log } from '$app/utils/log';
|
||||||
|
|
||||||
export type CellByFieldId = Map<string, CellIdentifier>;
|
export type CellByFieldId = Map<string, CellIdentifier>;
|
||||||
|
|
||||||
@ -82,13 +82,13 @@ export class RowCache {
|
|||||||
this.notifier.withChange(RowChangedReason.ReorderRows);
|
this.notifier.withChange(RowChangedReason.ReorderRows);
|
||||||
};
|
};
|
||||||
|
|
||||||
applyRowsChanged = (changeset: RowsChangesetPB) => {
|
applyRowsChanged = (changeset: RowsChangePB) => {
|
||||||
this._deleteRows(changeset.deleted_rows);
|
this._deleteRows(changeset.deleted_rows);
|
||||||
this._insertRows(changeset.inserted_rows);
|
this._insertRows(changeset.inserted_rows);
|
||||||
this._updateRows(changeset.updated_rows);
|
this._updateRows(changeset.updated_rows);
|
||||||
};
|
};
|
||||||
|
|
||||||
applyRowsVisibility = (changeset: RowsVisibilityChangesetPB) => {
|
applyRowsVisibility = (changeset: RowsVisibilityChangePB) => {
|
||||||
this._hideRows(changeset.invisible_rows);
|
this._hideRows(changeset.invisible_rows);
|
||||||
this._displayRows(changeset.visible_rows);
|
this._displayRows(changeset.visible_rows);
|
||||||
};
|
};
|
||||||
@ -339,8 +339,7 @@ export class RowInfo {
|
|||||||
public readonly viewId: string,
|
public readonly viewId: string,
|
||||||
public readonly fieldInfos: readonly FieldInfo[],
|
public readonly fieldInfos: readonly FieldInfo[],
|
||||||
public readonly row: RowPB
|
public readonly row: RowPB
|
||||||
) {
|
) {}
|
||||||
}
|
|
||||||
|
|
||||||
copyWith = (params: { row?: RowPB; fieldInfos?: readonly FieldInfo[] }) => {
|
copyWith = (params: { row?: RowPB; fieldInfos?: readonly FieldInfo[] }) => {
|
||||||
return new RowInfo(this.viewId, params.fieldInfos || this.fieldInfos, params.row || this.row);
|
return new RowInfo(this.viewId, params.fieldInfos || this.fieldInfos, params.row || this.row);
|
||||||
@ -348,18 +347,15 @@ export class RowInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class DeletedRow {
|
export class DeletedRow {
|
||||||
constructor(public readonly index: number, public readonly rowInfo: RowInfo) {
|
constructor(public readonly index: number, public readonly rowInfo: RowInfo) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InsertedRow {
|
export class InsertedRow {
|
||||||
constructor(public readonly index: number, public readonly rowId: string) {
|
constructor(public readonly index: number, public readonly rowId: string) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RowChanged {
|
export class RowChanged {
|
||||||
constructor(public readonly reason: RowChangedReason, public readonly rowId?: string) {
|
constructor(public readonly reason: RowChangedReason, public readonly rowId?: string) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-shadow
|
// eslint-disable-next-line no-shadow
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import { Ok, Result } from "ts-results";
|
import { Ok, Result } from 'ts-results';
|
||||||
import {
|
import {
|
||||||
DatabaseNotification,
|
DatabaseNotification,
|
||||||
FlowyError,
|
FlowyError,
|
||||||
ReorderAllRowsPB,
|
ReorderAllRowsPB,
|
||||||
ReorderSingleRowPB,
|
ReorderSingleRowPB,
|
||||||
RowsChangesetPB,
|
RowsChangePB,
|
||||||
RowsVisibilityChangesetPB
|
RowsVisibilityChangePB,
|
||||||
} from "@/services/backend";
|
} from '@/services/backend';
|
||||||
import { ChangeNotifier } from "$app/utils/change_notifier";
|
import { ChangeNotifier } from '$app/utils/change_notifier';
|
||||||
import { DatabaseNotificationObserver } from "../notifications/observer";
|
import { DatabaseNotificationObserver } from '../notifications/observer';
|
||||||
|
|
||||||
export type RowsVisibilityNotifyValue = Result<RowsVisibilityChangesetPB, FlowyError>;
|
export type RowsVisibilityNotifyValue = Result<RowsVisibilityChangePB, FlowyError>;
|
||||||
export type RowsNotifyValue = Result<RowsChangesetPB, FlowyError>;
|
export type RowsNotifyValue = Result<RowsChangePB, FlowyError>;
|
||||||
export type ReorderRowsNotifyValue = Result<string[], FlowyError>;
|
export type ReorderRowsNotifyValue = Result<string[], FlowyError>;
|
||||||
export type ReorderSingleRowNotifyValue = Result<ReorderSingleRowPB, FlowyError>;
|
export type ReorderSingleRowNotifyValue = Result<ReorderSingleRowPB, FlowyError>;
|
||||||
|
|
||||||
@ -23,8 +23,7 @@ export class DatabaseViewRowsObserver {
|
|||||||
|
|
||||||
private _listener?: DatabaseNotificationObserver;
|
private _listener?: DatabaseNotificationObserver;
|
||||||
|
|
||||||
constructor(public readonly viewId: string) {
|
constructor(public readonly viewId: string) {}
|
||||||
}
|
|
||||||
|
|
||||||
subscribe = async (callbacks: {
|
subscribe = async (callbacks: {
|
||||||
onRowsVisibilityChanged?: (value: RowsVisibilityNotifyValue) => void;
|
onRowsVisibilityChanged?: (value: RowsVisibilityNotifyValue) => void;
|
||||||
@ -44,14 +43,14 @@ export class DatabaseViewRowsObserver {
|
|||||||
switch (notification) {
|
switch (notification) {
|
||||||
case DatabaseNotification.DidUpdateViewRowsVisibility:
|
case DatabaseNotification.DidUpdateViewRowsVisibility:
|
||||||
if (result.ok) {
|
if (result.ok) {
|
||||||
this.rowsVisibilityNotifier.notify(Ok(RowsVisibilityChangesetPB.deserializeBinary(result.val)));
|
this.rowsVisibilityNotifier.notify(Ok(RowsVisibilityChangePB.deserializeBinary(result.val)));
|
||||||
} else {
|
} else {
|
||||||
this.rowsVisibilityNotifier.notify(result);
|
this.rowsVisibilityNotifier.notify(result);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DatabaseNotification.DidUpdateViewRows:
|
case DatabaseNotification.DidUpdateViewRows:
|
||||||
if (result.ok) {
|
if (result.ok) {
|
||||||
this.rowsNotifier.notify(Ok(RowsChangesetPB.deserializeBinary(result.val)));
|
this.rowsNotifier.notify(Ok(RowsChangePB.deserializeBinary(result.val)));
|
||||||
} else {
|
} else {
|
||||||
this.rowsNotifier.notify(result);
|
this.rowsNotifier.notify(result);
|
||||||
}
|
}
|
||||||
@ -73,7 +72,7 @@ export class DatabaseViewRowsObserver {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
await this._listener.start();
|
await this._listener.start();
|
||||||
};
|
};
|
||||||
|
20
frontend/rust-lib/Cargo.lock
generated
20
frontend/rust-lib/Cargo.lock
generated
@ -85,7 +85,7 @@ checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "appflowy-integrate"
|
name = "appflowy-integrate"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -886,7 +886,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab"
|
name = "collab"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -903,7 +903,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-client-ws"
|
name = "collab-client-ws"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"collab-sync",
|
"collab-sync",
|
||||||
@ -921,7 +921,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-database"
|
name = "collab-database"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -946,7 +946,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-derive"
|
name = "collab-derive"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -958,7 +958,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-document"
|
name = "collab-document"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -975,7 +975,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-folder"
|
name = "collab-folder"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -993,7 +993,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-persistence"
|
name = "collab-persistence"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -1013,7 +1013,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-plugins"
|
name = "collab-plugins"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1043,7 +1043,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-sync"
|
name = "collab-sync"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d6af3a#d6af3a7662a57202e4e5b0a297c28757a18f3372"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=173661#173661cc07cc78f4251983fcf7594533ba63bb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"collab",
|
"collab",
|
||||||
|
@ -33,11 +33,11 @@ opt-level = 3
|
|||||||
incremental = false
|
incremental = false
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d6af3a" }
|
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "173661" }
|
||||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d6af3a" }
|
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "173661" }
|
||||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d6af3a" }
|
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "173661" }
|
||||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d6af3a" }
|
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "173661" }
|
||||||
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d6af3a" }
|
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "173661" }
|
||||||
|
|
||||||
#collab = { path = "../AppFlowy-Collab/collab" }
|
#collab = { path = "../AppFlowy-Collab/collab" }
|
||||||
#collab-folder = { path = "../AppFlowy-Collab/collab-folder" }
|
#collab-folder = { path = "../AppFlowy-Collab/collab-folder" }
|
||||||
|
@ -142,11 +142,15 @@ pub struct RowIdPB {
|
|||||||
|
|
||||||
#[pb(index = 2)]
|
#[pb(index = 2)]
|
||||||
pub row_id: String,
|
pub row_id: String,
|
||||||
|
|
||||||
|
#[pb(index = 3, one_of)]
|
||||||
|
pub group_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RowIdParams {
|
pub struct RowIdParams {
|
||||||
pub view_id: String,
|
pub view_id: String,
|
||||||
pub row_id: RowId,
|
pub row_id: RowId,
|
||||||
|
pub group_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryInto<RowIdParams> for RowIdPB {
|
impl TryInto<RowIdParams> for RowIdPB {
|
||||||
@ -154,10 +158,19 @@ impl TryInto<RowIdParams> for RowIdPB {
|
|||||||
|
|
||||||
fn try_into(self) -> Result<RowIdParams, Self::Error> {
|
fn try_into(self) -> Result<RowIdParams, Self::Error> {
|
||||||
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseIdIsEmpty)?;
|
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseIdIsEmpty)?;
|
||||||
|
let group_id = match self.group_id {
|
||||||
|
Some(group_id) => Some(
|
||||||
|
NotEmptyStr::parse(group_id)
|
||||||
|
.map_err(|_| ErrorCode::GroupIdIsEmpty)?
|
||||||
|
.0,
|
||||||
|
),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
Ok(RowIdParams {
|
Ok(RowIdParams {
|
||||||
view_id: view_id.0,
|
view_id: view_id.0,
|
||||||
row_id: RowId::from(self.row_id),
|
row_id: RowId::from(self.row_id),
|
||||||
|
group_id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use flowy_derive::ProtoBuf;
|
|||||||
use crate::entities::{InsertedRowPB, UpdatedRowPB};
|
use crate::entities::{InsertedRowPB, UpdatedRowPB};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, ProtoBuf)]
|
#[derive(Debug, Default, Clone, ProtoBuf)]
|
||||||
pub struct RowsVisibilityChangesetPB {
|
pub struct RowsVisibilityChangePB {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
pub view_id: String,
|
pub view_id: String,
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ pub struct RowsVisibilityChangesetPB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, ProtoBuf)]
|
#[derive(Debug, Default, Clone, ProtoBuf)]
|
||||||
pub struct RowsChangesetPB {
|
pub struct RowsChangePB {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
pub view_id: String,
|
pub view_id: String,
|
||||||
|
|
||||||
@ -29,27 +29,27 @@ pub struct RowsChangesetPB {
|
|||||||
pub updated_rows: Vec<UpdatedRowPB>,
|
pub updated_rows: Vec<UpdatedRowPB>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RowsChangesetPB {
|
impl RowsChangePB {
|
||||||
pub fn from_insert(view_id: String, inserted_rows: Vec<InsertedRowPB>) -> Self {
|
pub fn from_insert(view_id: String, inserted_row: InsertedRowPB) -> Self {
|
||||||
Self {
|
Self {
|
||||||
view_id,
|
view_id,
|
||||||
inserted_rows,
|
inserted_rows: vec![inserted_row],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_delete(view_id: String, deleted_rows: Vec<String>) -> Self {
|
pub fn from_delete(view_id: String, deleted_row: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
view_id,
|
view_id,
|
||||||
deleted_rows,
|
deleted_rows: vec![deleted_row],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_update(view_id: String, updated_rows: Vec<UpdatedRowPB>) -> Self {
|
pub fn from_update(view_id: String, updated_row: UpdatedRowPB) -> Self {
|
||||||
Self {
|
Self {
|
||||||
view_id,
|
view_id,
|
||||||
updated_rows,
|
updated_rows: vec![updated_row],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
|
use collab_database::database::gen_row_id;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use collab_database::rows::RowId;
|
use collab_database::rows::RowId;
|
||||||
use collab_database::views::DatabaseLayout;
|
use collab_database::views::DatabaseLayout;
|
||||||
|
use lib_infra::util::timestamp;
|
||||||
|
|
||||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||||
use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataResult};
|
use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataResult};
|
||||||
|
|
||||||
use crate::entities::*;
|
use crate::entities::*;
|
||||||
use crate::manager::DatabaseManager2;
|
use crate::manager::DatabaseManager2;
|
||||||
|
use crate::services::cell::CellBuilder;
|
||||||
|
|
||||||
use crate::services::field::{
|
use crate::services::field::{
|
||||||
type_option_data_from_pb_or_default, DateCellChangeset, SelectOptionCellChangeset,
|
type_option_data_from_pb_or_default, DateCellChangeset, SelectOptionCellChangeset,
|
||||||
@ -304,7 +307,7 @@ pub(crate) async fn duplicate_row_handler(
|
|||||||
let params: RowIdParams = data.into_inner().try_into()?;
|
let params: RowIdParams = data.into_inner().try_into()?;
|
||||||
let database_editor = manager.get_database_with_view_id(¶ms.view_id).await?;
|
let database_editor = manager.get_database_with_view_id(¶ms.view_id).await?;
|
||||||
database_editor
|
database_editor
|
||||||
.duplicate_row(¶ms.view_id, ¶ms.row_id)
|
.duplicate_row(¶ms.view_id, params.group_id, ¶ms.row_id)
|
||||||
.await;
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -329,7 +332,23 @@ pub(crate) async fn create_row_handler(
|
|||||||
) -> DataResult<RowPB, FlowyError> {
|
) -> DataResult<RowPB, FlowyError> {
|
||||||
let params: CreateRowParams = data.into_inner().try_into()?;
|
let params: CreateRowParams = data.into_inner().try_into()?;
|
||||||
let database_editor = manager.get_database_with_view_id(¶ms.view_id).await?;
|
let database_editor = manager.get_database_with_view_id(¶ms.view_id).await?;
|
||||||
match database_editor.create_row(params).await? {
|
let fields = database_editor.get_fields(¶ms.view_id, None);
|
||||||
|
let cells =
|
||||||
|
CellBuilder::with_cells(params.cell_data_by_field_id.unwrap_or_default(), &fields).build();
|
||||||
|
let view_id = params.view_id;
|
||||||
|
let group_id = params.group_id;
|
||||||
|
let params = collab_database::rows::CreateRowParams {
|
||||||
|
id: gen_row_id(),
|
||||||
|
cells,
|
||||||
|
height: 60,
|
||||||
|
visibility: true,
|
||||||
|
prev_row_id: params.start_row_id,
|
||||||
|
timestamp: timestamp(),
|
||||||
|
};
|
||||||
|
match database_editor
|
||||||
|
.create_row(&view_id, group_id, params)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
None => Err(FlowyError::internal().context("Create row fail")),
|
None => Err(FlowyError::internal().context("Create row fail")),
|
||||||
Some(row) => data_result_ok(RowPB::from(row)),
|
Some(row) => data_result_ok(RowPB::from(row)),
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,9 @@ use std::ops::Deref;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use collab_database::database::{gen_row_id, timestamp, Database as InnerDatabase};
|
use collab_database::database::{timestamp, Database as InnerDatabase};
|
||||||
use collab_database::fields::{Field, TypeOptionData};
|
use collab_database::fields::{Field, TypeOptionData};
|
||||||
use collab_database::rows::{Cell, Cells, Row, RowCell, RowId};
|
use collab_database::rows::{Cell, Cells, CreateRowParams, Row, RowCell, RowId};
|
||||||
use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting};
|
use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use tokio::sync::{broadcast, RwLock};
|
use tokio::sync::{broadcast, RwLock};
|
||||||
@ -16,14 +16,14 @@ use lib_infra::future::{to_fut, Fut};
|
|||||||
|
|
||||||
use crate::entities::{
|
use crate::entities::{
|
||||||
AlterFilterParams, AlterSortParams, CalendarEventPB, CellChangesetNotifyPB, CellPB,
|
AlterFilterParams, AlterSortParams, CalendarEventPB, CellChangesetNotifyPB, CellPB,
|
||||||
CreateRowParams, DatabaseFieldChangesetPB, DatabasePB, DatabaseViewSettingPB, DeleteFilterParams,
|
DatabaseFieldChangesetPB, DatabasePB, DatabaseViewSettingPB, DeleteFilterParams,
|
||||||
DeleteGroupParams, DeleteSortParams, FieldChangesetParams, FieldIdPB, FieldPB, FieldType,
|
DeleteGroupParams, DeleteSortParams, FieldChangesetParams, FieldIdPB, FieldPB, FieldType,
|
||||||
GroupPB, IndexFieldPB, InsertGroupParams, InsertedRowPB, LayoutSettingParams, RepeatedFilterPB,
|
GroupPB, IndexFieldPB, InsertGroupParams, InsertedRowPB, LayoutSettingParams, RepeatedFilterPB,
|
||||||
RepeatedGroupPB, RepeatedSortPB, RowPB, RowsChangesetPB, SelectOptionCellDataPB, SelectOptionPB,
|
RepeatedGroupPB, RepeatedSortPB, RowPB, RowsChangePB, SelectOptionCellDataPB, SelectOptionPB,
|
||||||
};
|
};
|
||||||
use crate::notification::{send_notification, DatabaseNotification};
|
use crate::notification::{send_notification, DatabaseNotification};
|
||||||
use crate::services::cell::{
|
use crate::services::cell::{
|
||||||
apply_cell_changeset, get_cell_protobuf, insert_date_cell, AnyTypeCache, CellBuilder, CellCache,
|
apply_cell_changeset, get_cell_protobuf, insert_date_cell, AnyTypeCache, CellCache,
|
||||||
ToCellChangeset,
|
ToCellChangeset,
|
||||||
};
|
};
|
||||||
use crate::services::database::util::database_view_setting_pb_from_view;
|
use crate::services::database::util::database_view_setting_pb_from_view;
|
||||||
@ -274,8 +274,17 @@ impl DatabaseEditor {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn duplicate_row(&self, view_id: &str, row_id: &RowId) {
|
// consider returning a result. But most of the time, it should be fine to just ignore the error.
|
||||||
let _ = self.database.lock().duplicate_row(view_id, row_id);
|
pub async fn duplicate_row(&self, view_id: &str, group_id: Option<String>, row_id: &RowId) {
|
||||||
|
let params = self.database.lock().duplicate_row(row_id);
|
||||||
|
match params {
|
||||||
|
None => {
|
||||||
|
tracing::warn!("Failed to duplicate row: {}", row_id);
|
||||||
|
},
|
||||||
|
Some(params) => {
|
||||||
|
let _ = self.create_row(view_id, group_id, params).await;
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn move_row(&self, view_id: &str, from: RowId, to: RowId) {
|
pub async fn move_row(&self, view_id: &str, from: RowId, to: RowId) {
|
||||||
@ -292,39 +301,30 @@ impl DatabaseEditor {
|
|||||||
|
|
||||||
let delete_row_id = from.into_inner();
|
let delete_row_id = from.into_inner();
|
||||||
let insert_row = InsertedRowPB::from(&row).with_index(to_index as i32);
|
let insert_row = InsertedRowPB::from(&row).with_index(to_index as i32);
|
||||||
let changeset =
|
let changes =
|
||||||
RowsChangesetPB::from_move(view_id.to_string(), vec![delete_row_id], vec![insert_row]);
|
RowsChangePB::from_move(view_id.to_string(), vec![delete_row_id], vec![insert_row]);
|
||||||
send_notification(view_id, DatabaseNotification::DidUpdateViewRows)
|
send_notification(view_id, DatabaseNotification::DidUpdateViewRows)
|
||||||
.payload(changeset)
|
.payload(changes)
|
||||||
.send();
|
.send();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_row(&self, params: CreateRowParams) -> FlowyResult<Option<Row>> {
|
pub async fn create_row(
|
||||||
let fields = self.database.lock().get_fields(¶ms.view_id, None);
|
&self,
|
||||||
let mut cells =
|
view_id: &str,
|
||||||
CellBuilder::with_cells(params.cell_data_by_field_id.unwrap_or_default(), &fields).build();
|
group_id: Option<String>,
|
||||||
|
mut params: CreateRowParams,
|
||||||
|
) -> FlowyResult<Option<Row>> {
|
||||||
for view in self.database_views.editors().await {
|
for view in self.database_views.editors().await {
|
||||||
view.v_will_create_row(&mut cells, ¶ms.group_id).await;
|
view.v_will_create_row(&mut params.cells, &group_id).await;
|
||||||
}
|
}
|
||||||
|
let result = self.database.lock().create_row_in_view(view_id, params);
|
||||||
let result = self.database.lock().create_row_in_view(
|
|
||||||
¶ms.view_id,
|
|
||||||
collab_database::rows::CreateRowParams {
|
|
||||||
id: gen_row_id(),
|
|
||||||
cells,
|
|
||||||
height: 60,
|
|
||||||
visibility: true,
|
|
||||||
prev_row_id: params.start_row_id,
|
|
||||||
timestamp: timestamp(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some((index, row_order)) = result {
|
if let Some((index, row_order)) = result {
|
||||||
|
tracing::trace!("create row: {:?} at {}", row_order, index);
|
||||||
let row = self.database.lock().get_row(&row_order.id);
|
let row = self.database.lock().get_row(&row_order.id);
|
||||||
if let Some(row) = row {
|
if let Some(row) = row {
|
||||||
for view in self.database_views.editors().await {
|
for view in self.database_views.editors().await {
|
||||||
view.v_did_create_row(&row, ¶ms.group_id, index).await;
|
view.v_did_create_row(&row, &group_id, index).await;
|
||||||
}
|
}
|
||||||
return Ok(Some(row));
|
return Ok(Some(row));
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![allow(clippy::while_let_loop)]
|
#![allow(clippy::while_let_loop)]
|
||||||
use crate::entities::{
|
use crate::entities::{
|
||||||
DatabaseViewSettingPB, FilterChangesetNotificationPB, GroupChangesetPB, GroupRowsNotificationPB,
|
DatabaseViewSettingPB, FilterChangesetNotificationPB, GroupChangesetPB, GroupRowsNotificationPB,
|
||||||
ReorderAllRowsPB, ReorderSingleRowPB, RowsVisibilityChangesetPB, SortChangesetNotificationPB,
|
ReorderAllRowsPB, ReorderSingleRowPB, RowsVisibilityChangePB, SortChangesetNotificationPB,
|
||||||
};
|
};
|
||||||
use crate::notification::{send_notification, DatabaseNotification};
|
use crate::notification::{send_notification, DatabaseNotification};
|
||||||
use crate::services::filter::FilterResultNotification;
|
use crate::services::filter::FilterResultNotification;
|
||||||
@ -38,7 +38,7 @@ impl DatabaseViewChangedReceiverRunner {
|
|||||||
.for_each(|changed| async {
|
.for_each(|changed| async {
|
||||||
match changed {
|
match changed {
|
||||||
DatabaseViewChanged::FilterNotification(notification) => {
|
DatabaseViewChanged::FilterNotification(notification) => {
|
||||||
let changeset = RowsVisibilityChangesetPB {
|
let changeset = RowsVisibilityChangePB {
|
||||||
view_id: notification.view_id,
|
view_id: notification.view_id,
|
||||||
visible_rows: notification.visible_rows,
|
visible_rows: notification.visible_rows,
|
||||||
invisible_rows: notification
|
invisible_rows: notification
|
||||||
|
@ -16,7 +16,7 @@ use crate::entities::{
|
|||||||
AlterFilterParams, AlterSortParams, CalendarEventPB, DeleteFilterParams, DeleteGroupParams,
|
AlterFilterParams, AlterSortParams, CalendarEventPB, DeleteFilterParams, DeleteGroupParams,
|
||||||
DeleteSortParams, FieldType, GroupChangesetPB, GroupPB, GroupRowsNotificationPB,
|
DeleteSortParams, FieldType, GroupChangesetPB, GroupPB, GroupRowsNotificationPB,
|
||||||
InsertGroupParams, InsertedGroupPB, InsertedRowPB, LayoutSettingPB, LayoutSettingParams, RowPB,
|
InsertGroupParams, InsertedGroupPB, InsertedRowPB, LayoutSettingPB, LayoutSettingParams, RowPB,
|
||||||
RowsChangesetPB, SortChangesetNotificationPB, SortPB,
|
RowsChangePB, SortChangesetNotificationPB, SortPB,
|
||||||
};
|
};
|
||||||
use crate::notification::{send_notification, DatabaseNotification};
|
use crate::notification::{send_notification, DatabaseNotification};
|
||||||
use crate::services::cell::CellCache;
|
use crate::services::cell::CellCache;
|
||||||
@ -182,9 +182,10 @@ impl DatabaseViewEditor {
|
|||||||
// Send the group notification if the current view has groups
|
// Send the group notification if the current view has groups
|
||||||
match group_id.as_ref() {
|
match group_id.as_ref() {
|
||||||
None => {
|
None => {
|
||||||
let changeset = RowsChangesetPB::from_insert(self.view_id.clone(), vec![row.into()]);
|
let row = InsertedRowPB::from(row).with_index(index as i32);
|
||||||
|
let changes = RowsChangePB::from_insert(self.view_id.clone(), row);
|
||||||
send_notification(&self.view_id, DatabaseNotification::DidUpdateViewRows)
|
send_notification(&self.view_id, DatabaseNotification::DidUpdateViewRows)
|
||||||
.payload(changeset)
|
.payload(changes)
|
||||||
.send();
|
.send();
|
||||||
},
|
},
|
||||||
Some(group_id) => {
|
Some(group_id) => {
|
||||||
@ -219,16 +220,15 @@ impl DatabaseViewEditor {
|
|||||||
notify_did_update_group_rows(changeset).await;
|
notify_did_update_group_rows(changeset).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let changeset =
|
let changes = RowsChangePB::from_delete(self.view_id.clone(), row.id.clone().into_inner());
|
||||||
RowsChangesetPB::from_delete(self.view_id.clone(), vec![row.id.clone().into_inner()]);
|
|
||||||
send_notification(&self.view_id, DatabaseNotification::DidUpdateViewRows)
|
send_notification(&self.view_id, DatabaseNotification::DidUpdateViewRows)
|
||||||
.payload(changeset)
|
.payload(changes)
|
||||||
.send();
|
.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Notify the view that the row has been updated. If the view has groups,
|
/// Notify the view that the row has been updated. If the view has groups,
|
||||||
/// send the group notification with [GroupRowsNotificationPB]. Otherwise,
|
/// send the group notification with [GroupRowsNotificationPB]. Otherwise,
|
||||||
/// send the view notification with [RowsChangesetPB]
|
/// send the view notification with [RowsChangePB]
|
||||||
pub async fn v_did_update_row(&self, old_row: &Option<Row>, row: &Row, field_id: &str) {
|
pub async fn v_did_update_row(&self, old_row: &Option<Row>, row: &Row, field_id: &str) {
|
||||||
let result = self
|
let result = self
|
||||||
.mut_group_controller(|group_controller, field| {
|
.mut_group_controller(|group_controller, field| {
|
||||||
@ -263,7 +263,7 @@ impl DatabaseViewEditor {
|
|||||||
row: RowOrder::from(row),
|
row: RowOrder::from(row),
|
||||||
field_ids: vec![field_id.to_string()],
|
field_ids: vec![field_id.to_string()],
|
||||||
};
|
};
|
||||||
let changeset = RowsChangesetPB::from_update(self.view_id.clone(), vec![update_row.into()]);
|
let changeset = RowsChangePB::from_update(self.view_id.clone(), update_row.into());
|
||||||
send_notification(&self.view_id, DatabaseNotification::DidUpdateViewRows)
|
send_notification(&self.view_id, DatabaseNotification::DidUpdateViewRows)
|
||||||
.payload(changeset)
|
.payload(changeset)
|
||||||
.send();
|
.send();
|
||||||
@ -784,18 +784,18 @@ impl DatabaseViewEditor {
|
|||||||
pub async fn handle_row_event(&self, event: Cow<'_, DatabaseRowEvent>) {
|
pub async fn handle_row_event(&self, event: Cow<'_, DatabaseRowEvent>) {
|
||||||
let changeset = match event.into_owned() {
|
let changeset = match event.into_owned() {
|
||||||
DatabaseRowEvent::InsertRow(row) => {
|
DatabaseRowEvent::InsertRow(row) => {
|
||||||
RowsChangesetPB::from_insert(self.view_id.clone(), vec![row.into()])
|
RowsChangePB::from_insert(self.view_id.clone(), row.into())
|
||||||
},
|
},
|
||||||
DatabaseRowEvent::UpdateRow(row) => {
|
DatabaseRowEvent::UpdateRow(row) => {
|
||||||
RowsChangesetPB::from_update(self.view_id.clone(), vec![row.into()])
|
RowsChangePB::from_update(self.view_id.clone(), row.into())
|
||||||
},
|
},
|
||||||
DatabaseRowEvent::DeleteRow(row_id) => {
|
DatabaseRowEvent::DeleteRow(row_id) => {
|
||||||
RowsChangesetPB::from_delete(self.view_id.clone(), vec![row_id.into_inner()])
|
RowsChangePB::from_delete(self.view_id.clone(), row_id.into_inner())
|
||||||
},
|
},
|
||||||
DatabaseRowEvent::Move {
|
DatabaseRowEvent::Move {
|
||||||
deleted_row_id,
|
deleted_row_id,
|
||||||
inserted_row,
|
inserted_row,
|
||||||
} => RowsChangesetPB::from_move(
|
} => RowsChangePB::from_move(
|
||||||
self.view_id.clone(),
|
self.view_id.clone(),
|
||||||
vec![deleted_row_id.into_inner()],
|
vec![deleted_row_id.into_inner()],
|
||||||
vec![inserted_row.into()],
|
vec![inserted_row.into()],
|
||||||
|
@ -16,8 +16,6 @@ mod tests {
|
|||||||
|
|
||||||
// Input is empty String
|
// Input is empty String
|
||||||
assert_number(&type_option, "", "", &field_type, &field);
|
assert_number(&type_option, "", "", &field_type, &field);
|
||||||
|
|
||||||
// Input is letter
|
|
||||||
assert_number(&type_option, "abc", "", &field_type, &field);
|
assert_number(&type_option, "abc", "", &field_type, &field);
|
||||||
assert_number(&type_option, "-123", "-123", &field_type, &field);
|
assert_number(&type_option, "-123", "-123", &field_type, &field);
|
||||||
assert_number(&type_option, "abc-123", "-123", &field_type, &field);
|
assert_number(&type_option, "abc-123", "-123", &field_type, &field);
|
||||||
@ -25,6 +23,7 @@ mod tests {
|
|||||||
assert_number(&type_option, "0.2", "0.2", &field_type, &field);
|
assert_number(&type_option, "0.2", "0.2", &field_type, &field);
|
||||||
assert_number(&type_option, "-0.2", "-0.2", &field_type, &field);
|
assert_number(&type_option, "-0.2", "-0.2", &field_type, &field);
|
||||||
assert_number(&type_option, "-$0.2", "0.2", &field_type, &field);
|
assert_number(&type_option, "-$0.2", "0.2", &field_type, &field);
|
||||||
|
assert_number(&type_option, ".2", "0.2", &field_type, &field);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -42,6 +41,7 @@ mod tests {
|
|||||||
assert_number(&type_option, "-0.2", "-$0.2", &field_type, &field);
|
assert_number(&type_option, "-0.2", "-$0.2", &field_type, &field);
|
||||||
assert_number(&type_option, "-$0.2", "-$0.2", &field_type, &field);
|
assert_number(&type_option, "-$0.2", "-$0.2", &field_type, &field);
|
||||||
assert_number(&type_option, "-€0.2", "-$0.2", &field_type, &field);
|
assert_number(&type_option, "-€0.2", "-$0.2", &field_type, &field);
|
||||||
|
assert_number(&type_option, ".2", "$0.2", &field_type, &field);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -128,12 +128,25 @@ impl NumberTypeOption {
|
|||||||
Err(_) => Ok(NumberCellFormat::new()),
|
Err(_) => Ok(NumberCellFormat::new()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let num_str = match EXTRACT_NUM_REGEX.captures(&num_cell_data.0) {
|
// Test the input string is start with dot and only contains number.
|
||||||
Ok(Some(captures)) => captures
|
// If it is, add a 0 before the dot. For example, ".123" -> "0.123"
|
||||||
.get(0)
|
let num_str = match START_WITH_DOT_NUM_REGEX.captures(&num_cell_data.0) {
|
||||||
.map(|m| m.as_str().to_string())
|
Ok(Some(captures)) => match captures.get(0).map(|m| m.as_str().to_string()) {
|
||||||
.unwrap_or_default(),
|
Some(s) => {
|
||||||
_ => "".to_string(),
|
format!("0{}", s)
|
||||||
|
},
|
||||||
|
None => "".to_string(),
|
||||||
|
},
|
||||||
|
// Extract the number from the string.
|
||||||
|
// For example, "123abc" -> "123". check out the number_type_option_input_test test for
|
||||||
|
// more examples.
|
||||||
|
_ => match EXTRACT_NUM_REGEX.captures(&num_cell_data.0) {
|
||||||
|
Ok(Some(captures)) => captures
|
||||||
|
.get(0)
|
||||||
|
.map(|m| m.as_str().to_string())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
_ => "".to_string(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
match Decimal::from_str(&num_str) {
|
match Decimal::from_str(&num_str) {
|
||||||
@ -142,7 +155,10 @@ impl NumberTypeOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => NumberCellFormat::from_format_str(&num_cell_data.0, &self.format),
|
_ => {
|
||||||
|
// If the format is not number, use the format string to format the number.
|
||||||
|
NumberCellFormat::from_format_str(&num_cell_data.0, &self.format)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,4 +277,5 @@ impl std::default::Default for NumberTypeOption {
|
|||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref SCIENTIFIC_NOTATION_REGEX: Regex = Regex::new(r"([+-]?\d*\.?\d+)e([+-]?\d+)").unwrap();
|
static ref SCIENTIFIC_NOTATION_REGEX: Regex = Regex::new(r"([+-]?\d*\.?\d+)e([+-]?\d+)").unwrap();
|
||||||
pub(crate) static ref EXTRACT_NUM_REGEX: Regex = Regex::new(r"-?\d+(\.\d+)?").unwrap();
|
pub(crate) static ref EXTRACT_NUM_REGEX: Regex = Regex::new(r"-?\d+(\.\d+)?").unwrap();
|
||||||
|
pub(crate) static ref START_WITH_DOT_NUM_REGEX: Regex = Regex::new(r"^\.\d+").unwrap();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::services::cell::{CellBytesCustomParser, CellProtobufBlobParser, DecodedCellData};
|
use crate::services::cell::{CellBytesCustomParser, CellProtobufBlobParser, DecodedCellData};
|
||||||
use crate::services::field::number_currency::Currency;
|
use crate::services::field::number_currency::Currency;
|
||||||
use crate::services::field::{NumberFormat, EXTRACT_NUM_REGEX};
|
use crate::services::field::{NumberFormat, EXTRACT_NUM_REGEX, START_WITH_DOT_NUM_REGEX};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use flowy_error::FlowyResult;
|
use flowy_error::FlowyResult;
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
@ -32,8 +32,8 @@ impl NumberCellFormat {
|
|||||||
Some(offset) => offset != 0,
|
Some(offset) => offset != 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Extract number from string.
|
let num_str = auto_fill_zero_at_start_if_need(num_str);
|
||||||
let num_str = extract_number(num_str);
|
let num_str = extract_number(&num_str);
|
||||||
match Decimal::from_str(&num_str) {
|
match Decimal::from_str(&num_str) {
|
||||||
Ok(mut decimal) => {
|
Ok(mut decimal) => {
|
||||||
decimal.set_sign_positive(sign_positive);
|
decimal.set_sign_positive(sign_positive);
|
||||||
@ -70,6 +70,16 @@ impl NumberCellFormat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn auto_fill_zero_at_start_if_need(num_str: &str) -> String {
|
||||||
|
match START_WITH_DOT_NUM_REGEX.captures(num_str) {
|
||||||
|
Ok(Some(captures)) => match captures.get(0).map(|m| m.as_str().to_string()) {
|
||||||
|
Some(s) => format!("0{}", s),
|
||||||
|
None => num_str.to_string(),
|
||||||
|
},
|
||||||
|
_ => num_str.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn extract_number(num_str: &str) -> String {
|
fn extract_number(num_str: &str) -> String {
|
||||||
let mut matches = EXTRACT_NUM_REGEX.find_iter(num_str);
|
let mut matches = EXTRACT_NUM_REGEX.find_iter(num_str);
|
||||||
let mut values = vec![];
|
let mut values = vec![];
|
||||||
|
@ -2,52 +2,48 @@ use crate::database::block_test::script::DatabaseRowTest;
|
|||||||
use crate::database::block_test::script::RowScript::*;
|
use crate::database::block_test::script::RowScript::*;
|
||||||
use flowy_database2::entities::FieldType;
|
use flowy_database2::entities::FieldType;
|
||||||
use flowy_database2::services::field::DateCellData;
|
use flowy_database2::services::field::DateCellData;
|
||||||
|
use lib_infra::util::timestamp;
|
||||||
|
|
||||||
|
// Create a new row at the end of the grid and check the create time is valid.
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn set_created_at_field_on_create_row() {
|
async fn created_at_field_test() {
|
||||||
let mut test = DatabaseRowTest::new().await;
|
let mut test = DatabaseRowTest::new().await;
|
||||||
let row_count = test.rows.len();
|
let row_count = test.rows.len();
|
||||||
|
|
||||||
let before_create_timestamp = chrono::offset::Utc::now().timestamp();
|
|
||||||
test
|
test
|
||||||
.run_scripts(vec![CreateEmptyRow, AssertRowCount(row_count + 1)])
|
.run_scripts(vec![CreateEmptyRow, AssertRowCount(row_count + 1)])
|
||||||
.await;
|
.await;
|
||||||
let after_create_timestamp = chrono::offset::Utc::now().timestamp();
|
|
||||||
|
|
||||||
let mut rows = test.rows.clone();
|
// Get created time of the new row.
|
||||||
rows.sort_by(|r1, r2| r1.created_at.cmp(&r2.created_at));
|
let row = test.get_rows().await.last().cloned().unwrap();
|
||||||
let row = rows.last().unwrap();
|
let updated_at_field = test.get_first_field(FieldType::CreatedAt);
|
||||||
|
let cell = row.cells.cell_for_field_id(&updated_at_field.id).unwrap();
|
||||||
let fields = test.fields.clone();
|
|
||||||
let created_at_field = fields
|
|
||||||
.iter()
|
|
||||||
.find(|&f| FieldType::from(f.field_type) == FieldType::CreatedAt)
|
|
||||||
.unwrap();
|
|
||||||
let cell = row.cells.cell_for_field_id(&created_at_field.id).unwrap();
|
|
||||||
let created_at_timestamp = DateCellData::from(cell).timestamp.unwrap();
|
let created_at_timestamp = DateCellData::from(cell).timestamp.unwrap();
|
||||||
|
|
||||||
assert!(
|
assert!(created_at_timestamp > 0);
|
||||||
created_at_timestamp >= before_create_timestamp
|
assert!(created_at_timestamp < timestamp());
|
||||||
&& created_at_timestamp <= after_create_timestamp,
|
}
|
||||||
"timestamp: {}, before: {}, after: {}",
|
|
||||||
created_at_timestamp,
|
// Update row and check the update time is valid.
|
||||||
before_create_timestamp,
|
#[tokio::test]
|
||||||
after_create_timestamp
|
async fn update_at_field_test() {
|
||||||
);
|
let mut test = DatabaseRowTest::new().await;
|
||||||
|
let row = test.get_rows().await.remove(0);
|
||||||
let updated_at_field = fields
|
let updated_at_field = test.get_first_field(FieldType::UpdatedAt);
|
||||||
.iter()
|
let cell = row.cells.cell_for_field_id(&updated_at_field.id).unwrap();
|
||||||
.find(|&f| FieldType::from(f.field_type) == FieldType::UpdatedAt)
|
let old_updated_at = DateCellData::from(cell).timestamp.unwrap();
|
||||||
.unwrap();
|
|
||||||
let cell = row.cells.cell_for_field_id(&updated_at_field.id).unwrap();
|
test
|
||||||
let updated_at_timestamp = DateCellData::from(cell).timestamp.unwrap();
|
.run_script(UpdateTextCell {
|
||||||
|
row_id: row.id.clone(),
|
||||||
assert!(
|
content: "test".to_string(),
|
||||||
updated_at_timestamp >= before_create_timestamp
|
})
|
||||||
&& updated_at_timestamp <= after_create_timestamp,
|
.await;
|
||||||
"timestamp: {}, before: {}, after: {}",
|
|
||||||
updated_at_timestamp,
|
// Get the updated time of the row.
|
||||||
before_create_timestamp,
|
let row = test.get_rows().await.remove(0);
|
||||||
after_create_timestamp
|
let updated_at_field = test.get_first_field(FieldType::UpdatedAt);
|
||||||
);
|
let cell = row.cells.cell_for_field_id(&updated_at_field.id).unwrap();
|
||||||
|
let new_updated_at = DateCellData::from(cell).timestamp.unwrap();
|
||||||
|
|
||||||
|
assert!(old_updated_at < new_updated_at);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
use crate::database::database_editor::DatabaseEditorTest;
|
use crate::database::database_editor::DatabaseEditorTest;
|
||||||
use flowy_database2::entities::CreateRowParams;
|
use collab_database::database::gen_row_id;
|
||||||
|
use collab_database::rows::RowId;
|
||||||
|
|
||||||
|
use lib_infra::util::timestamp;
|
||||||
|
|
||||||
pub enum RowScript {
|
pub enum RowScript {
|
||||||
CreateEmptyRow,
|
CreateEmptyRow,
|
||||||
|
UpdateTextCell { row_id: RowId, content: String },
|
||||||
AssertRowCount(usize),
|
AssertRowCount(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,18 +29,25 @@ impl DatabaseRowTest {
|
|||||||
pub async fn run_script(&mut self, script: RowScript) {
|
pub async fn run_script(&mut self, script: RowScript) {
|
||||||
match script {
|
match script {
|
||||||
RowScript::CreateEmptyRow => {
|
RowScript::CreateEmptyRow => {
|
||||||
let params = CreateRowParams {
|
let params = collab_database::rows::CreateRowParams {
|
||||||
view_id: self.view_id.clone(),
|
id: gen_row_id(),
|
||||||
start_row_id: None,
|
timestamp: timestamp(),
|
||||||
group_id: None,
|
..Default::default()
|
||||||
cell_data_by_field_id: None,
|
|
||||||
};
|
};
|
||||||
let row_order = self.editor.create_row(params).await.unwrap().unwrap();
|
let row_order = self
|
||||||
|
.editor
|
||||||
|
.create_row(&self.view_id, None, params)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
self
|
self
|
||||||
.row_by_row_id
|
.row_by_row_id
|
||||||
.insert(row_order.id.to_string(), row_order.into());
|
.insert(row_order.id.to_string(), row_order.into());
|
||||||
self.rows = self.get_rows().await;
|
self.rows = self.get_rows().await;
|
||||||
},
|
},
|
||||||
|
RowScript::UpdateTextCell { row_id, content } => {
|
||||||
|
self.update_text_cell(row_id, &content).await.unwrap();
|
||||||
|
},
|
||||||
RowScript::AssertRowCount(expected_row_count) => {
|
RowScript::AssertRowCount(expected_row_count) => {
|
||||||
assert_eq!(expected_row_count, self.rows.len());
|
assert_eq!(expected_row_count, self.rows.len());
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
use collab_database::database::gen_row_id;
|
||||||
use collab_database::fields::Field;
|
use collab_database::fields::Field;
|
||||||
use collab_database::rows::RowId;
|
use collab_database::rows::{CreateRowParams, RowId};
|
||||||
use flowy_database2::entities::{CreateRowParams, FieldType, GroupPB, RowPB};
|
use flowy_database2::entities::{FieldType, GroupPB, RowPB};
|
||||||
use flowy_database2::services::cell::{
|
use flowy_database2::services::cell::{
|
||||||
delete_select_option_cell, insert_select_option_cell, insert_url_cell,
|
delete_select_option_cell, insert_select_option_cell, insert_url_cell,
|
||||||
};
|
};
|
||||||
@ -8,6 +9,7 @@ use flowy_database2::services::field::{
|
|||||||
edit_single_select_type_option, SelectOption, SelectTypeOptionSharedAction,
|
edit_single_select_type_option, SelectOption, SelectTypeOptionSharedAction,
|
||||||
SingleSelectTypeOption,
|
SingleSelectTypeOption,
|
||||||
};
|
};
|
||||||
|
use lib_infra::util::timestamp;
|
||||||
|
|
||||||
use crate::database::database_editor::DatabaseEditorTest;
|
use crate::database::database_editor::DatabaseEditorTest;
|
||||||
|
|
||||||
@ -126,12 +128,15 @@ impl DatabaseGroupTest {
|
|||||||
GroupScript::CreateRow { group_index } => {
|
GroupScript::CreateRow { group_index } => {
|
||||||
let group = self.group_at_index(group_index).await;
|
let group = self.group_at_index(group_index).await;
|
||||||
let params = CreateRowParams {
|
let params = CreateRowParams {
|
||||||
view_id: self.view_id.clone(),
|
id: gen_row_id(),
|
||||||
start_row_id: None,
|
timestamp: timestamp(),
|
||||||
group_id: Some(group.group_id.clone()),
|
..Default::default()
|
||||||
cell_data_by_field_id: None,
|
|
||||||
};
|
};
|
||||||
let _ = self.editor.create_row(params).await.unwrap();
|
let _ = self
|
||||||
|
.editor
|
||||||
|
.create_row(&self.view_id, Some(group.group_id.clone()), params)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
},
|
},
|
||||||
GroupScript::DeleteRow {
|
GroupScript::DeleteRow {
|
||||||
group_index,
|
group_index,
|
||||||
|
@ -264,14 +264,7 @@ pub fn make_test_grid() -> DatabaseData {
|
|||||||
database_id: gen_database_id(),
|
database_id: gen_database_id(),
|
||||||
name: "".to_string(),
|
name: "".to_string(),
|
||||||
layout: DatabaseLayout::Grid,
|
layout: DatabaseLayout::Grid,
|
||||||
layout_settings: Default::default(),
|
..Default::default()
|
||||||
filters: vec![],
|
|
||||||
group_settings: vec![],
|
|
||||||
sorts: vec![],
|
|
||||||
row_orders: vec![],
|
|
||||||
field_orders: vec![],
|
|
||||||
created_at: 0,
|
|
||||||
modified_at: 0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DatabaseData { view, fields, rows }
|
DatabaseData { view, fields, rows }
|
||||||
|
Reference in New Issue
Block a user