mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: show grid
This commit is contained in:
parent
9bb516786e
commit
05441f865e
@ -52,7 +52,7 @@ abstract class PluginBuilder {
|
||||
|
||||
PluginType get pluginType;
|
||||
|
||||
ViewDataType get dataType => ViewDataType.PlainText;
|
||||
ViewDataType get dataType => ViewDataType.Block;
|
||||
}
|
||||
|
||||
abstract class PluginConfig {
|
||||
|
@ -85,7 +85,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
||||
});
|
||||
|
||||
listener.start();
|
||||
final result = await service.openDocument(docId: view.id, dataType: view.dataType);
|
||||
final result = await service.openDocument(docId: view.id);
|
||||
result.fold(
|
||||
(block) {
|
||||
document = _decodeJsonToDocument(block.deltaStr);
|
||||
|
@ -7,17 +7,18 @@ import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
class DocumentService {
|
||||
Future<Either<BlockDelta, FlowyError>> openDocument({
|
||||
required String docId,
|
||||
required ViewDataType dataType,
|
||||
}) {
|
||||
final request = ViewId(value: docId);
|
||||
return FolderEventOpenView(request).send();
|
||||
}) async {
|
||||
await FolderEventSetLatestView(ViewId(value: docId)).send();
|
||||
|
||||
final payload = BlockId(value: docId);
|
||||
return BlockEventGetBlockData(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<BlockDelta, FlowyError>> composeDelta({required String docId, required String data}) {
|
||||
final request = BlockDelta.create()
|
||||
final payload = BlockDelta.create()
|
||||
..blockId = docId
|
||||
..deltaStr = data;
|
||||
return FolderEventApplyDocDelta(request).send();
|
||||
return FolderEventApplyDocDelta(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> closeDocument({required String docId}) {
|
||||
|
@ -1,12 +1,15 @@
|
||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
class GridService {
|
||||
Future<Either<Grid, FlowyError>> openGrid({required String gridId}) {
|
||||
Future<Either<Grid, FlowyError>> openGrid({required String gridId}) async {
|
||||
await FolderEventSetLatestView(ViewId(value: gridId)).send();
|
||||
|
||||
final payload = GridId(value: gridId);
|
||||
return GridEventOpenGrid(payload).send();
|
||||
return GridEventGetGridData(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<void, FlowyError>> createRow({required String gridId}) {
|
||||
|
@ -50,7 +50,7 @@ class DocumentPluginBuilder extends PluginBuilder {
|
||||
PluginType get pluginType => DefaultPlugin.quill.type();
|
||||
|
||||
@override
|
||||
ViewDataType get dataType => ViewDataType.RichText;
|
||||
ViewDataType get dataType => ViewDataType.Block;
|
||||
}
|
||||
|
||||
class DocumentPlugin implements Plugin {
|
||||
|
@ -17,7 +17,7 @@ class GridPluginBuilder implements PluginBuilder {
|
||||
}
|
||||
|
||||
@override
|
||||
String get menuName => "Table";
|
||||
String get menuName => "Grid";
|
||||
|
||||
@override
|
||||
PluginType get pluginType => DefaultPlugin.grid.type();
|
||||
|
@ -1,13 +1,30 @@
|
||||
|
||||
/// Auto generate. Do not edit
|
||||
part of '../../dispatch.dart';
|
||||
class BlockEventApplyDocDelta {
|
||||
BlockDelta request;
|
||||
BlockEventApplyDocDelta(this.request);
|
||||
class BlockEventGetBlockData {
|
||||
BlockId request;
|
||||
BlockEventGetBlockData(this.request);
|
||||
|
||||
Future<Either<BlockDelta, FlowyError>> send() {
|
||||
final request = FFIRequest.create()
|
||||
..event = BlockEvent.ApplyDocDelta.toString()
|
||||
..event = BlockEvent.GetBlockData.toString()
|
||||
..payload = requestToBytes(this.request);
|
||||
|
||||
return Dispatch.asyncRequest(request)
|
||||
.then((bytesResult) => bytesResult.fold(
|
||||
(okBytes) => left(BlockDelta.fromBuffer(okBytes)),
|
||||
(errBytes) => right(FlowyError.fromBuffer(errBytes)),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
class BlockEventApplyDelta {
|
||||
BlockDelta request;
|
||||
BlockEventApplyDelta(this.request);
|
||||
|
||||
Future<Either<BlockDelta, FlowyError>> send() {
|
||||
final request = FFIRequest.create()
|
||||
..event = BlockEvent.ApplyDelta.toString()
|
||||
..payload = requestToBytes(this.request);
|
||||
|
||||
return Dispatch.asyncRequest(request)
|
||||
|
@ -267,18 +267,18 @@ class FolderEventCopyLink {
|
||||
}
|
||||
}
|
||||
|
||||
class FolderEventOpenView {
|
||||
class FolderEventSetLatestView {
|
||||
ViewId request;
|
||||
FolderEventOpenView(this.request);
|
||||
FolderEventSetLatestView(this.request);
|
||||
|
||||
Future<Either<BlockDelta, FlowyError>> send() {
|
||||
Future<Either<Unit, FlowyError>> send() {
|
||||
final request = FFIRequest.create()
|
||||
..event = FolderEvent.OpenView.toString()
|
||||
..event = FolderEvent.SetLatestView.toString()
|
||||
..payload = requestToBytes(this.request);
|
||||
|
||||
return Dispatch.asyncRequest(request)
|
||||
.then((bytesResult) => bytesResult.fold(
|
||||
(okBytes) => left(BlockDelta.fromBuffer(okBytes)),
|
||||
(bytes) => left(unit),
|
||||
(errBytes) => right(FlowyError.fromBuffer(errBytes)),
|
||||
));
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
|
||||
/// Auto generate. Do not edit
|
||||
part of '../../dispatch.dart';
|
||||
class GridEventOpenGrid {
|
||||
class GridEventGetGridData {
|
||||
GridId request;
|
||||
GridEventOpenGrid(this.request);
|
||||
GridEventGetGridData(this.request);
|
||||
|
||||
Future<Either<Grid, FlowyError>> send() {
|
||||
final request = FFIRequest.create()
|
||||
..event = GridEvent.OpenGrid.toString()
|
||||
..event = GridEvent.GetGridData.toString()
|
||||
..payload = requestToBytes(this.request);
|
||||
|
||||
return Dispatch.asyncRequest(request)
|
||||
|
@ -10,11 +10,13 @@ import 'dart:core' as $core;
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
class BlockEvent extends $pb.ProtobufEnum {
|
||||
static const BlockEvent ApplyDocDelta = BlockEvent._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ApplyDocDelta');
|
||||
static const BlockEvent ExportDocument = BlockEvent._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ExportDocument');
|
||||
static const BlockEvent GetBlockData = BlockEvent._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetBlockData');
|
||||
static const BlockEvent ApplyDelta = BlockEvent._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ApplyDelta');
|
||||
static const BlockEvent ExportDocument = BlockEvent._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ExportDocument');
|
||||
|
||||
static const $core.List<BlockEvent> values = <BlockEvent> [
|
||||
ApplyDocDelta,
|
||||
GetBlockData,
|
||||
ApplyDelta,
|
||||
ExportDocument,
|
||||
];
|
||||
|
||||
|
@ -12,10 +12,11 @@ import 'dart:typed_data' as $typed_data;
|
||||
const BlockEvent$json = const {
|
||||
'1': 'BlockEvent',
|
||||
'2': const [
|
||||
const {'1': 'ApplyDocDelta', '2': 0},
|
||||
const {'1': 'ExportDocument', '2': 1},
|
||||
const {'1': 'GetBlockData', '2': 0},
|
||||
const {'1': 'ApplyDelta', '2': 1},
|
||||
const {'1': 'ExportDocument', '2': 2},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `BlockEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||
final $typed_data.Uint8List blockEventDescriptor = $convert.base64Decode('CgpCbG9ja0V2ZW50EhEKDUFwcGx5RG9jRGVsdGEQABISCg5FeHBvcnREb2N1bWVudBAB');
|
||||
final $typed_data.Uint8List blockEventDescriptor = $convert.base64Decode('CgpCbG9ja0V2ZW50EhAKDEdldEJsb2NrRGF0YRAAEg4KCkFwcGx5RGVsdGEQARISCg5FeHBvcnREb2N1bWVudBAC');
|
||||
|
@ -20,7 +20,7 @@ class View extends $pb.GeneratedMessage {
|
||||
..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'belongToId')
|
||||
..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
|
||||
..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
|
||||
..e<ViewDataType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dataType', $pb.PbFieldType.OE, defaultOrMaker: ViewDataType.RichText, valueOf: ViewDataType.valueOf, enumValues: ViewDataType.values)
|
||||
..e<ViewDataType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dataType', $pb.PbFieldType.OE, defaultOrMaker: ViewDataType.Block, valueOf: ViewDataType.valueOf, enumValues: ViewDataType.values)
|
||||
..aInt64(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'version')
|
||||
..aOM<RepeatedView>(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'belongings', subBuilder: RepeatedView.create)
|
||||
..aInt64(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'modifiedTime')
|
||||
@ -274,7 +274,7 @@ class CreateViewPayload extends $pb.GeneratedMessage {
|
||||
..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
|
||||
..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
|
||||
..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'thumbnail')
|
||||
..e<ViewDataType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dataType', $pb.PbFieldType.OE, defaultOrMaker: ViewDataType.RichText, valueOf: ViewDataType.valueOf, enumValues: ViewDataType.values)
|
||||
..e<ViewDataType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dataType', $pb.PbFieldType.OE, defaultOrMaker: ViewDataType.Block, valueOf: ViewDataType.valueOf, enumValues: ViewDataType.values)
|
||||
..aOS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'extData')
|
||||
..a<$core.int>(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'pluginType', $pb.PbFieldType.O3)
|
||||
..hasRequiredFields = false
|
||||
@ -408,7 +408,7 @@ class CreateViewParams extends $pb.GeneratedMessage {
|
||||
..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
|
||||
..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
|
||||
..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'thumbnail')
|
||||
..e<ViewDataType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dataType', $pb.PbFieldType.OE, defaultOrMaker: ViewDataType.RichText, valueOf: ViewDataType.valueOf, enumValues: ViewDataType.values)
|
||||
..e<ViewDataType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dataType', $pb.PbFieldType.OE, defaultOrMaker: ViewDataType.Block, valueOf: ViewDataType.valueOf, enumValues: ViewDataType.values)
|
||||
..aOS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'extData')
|
||||
..aOS(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewId')
|
||||
..aOS(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data')
|
||||
|
@ -10,13 +10,11 @@ import 'dart:core' as $core;
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
class ViewDataType extends $pb.ProtobufEnum {
|
||||
static const ViewDataType RichText = ViewDataType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'RichText');
|
||||
static const ViewDataType PlainText = ViewDataType._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'PlainText');
|
||||
static const ViewDataType Grid = ViewDataType._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Grid');
|
||||
static const ViewDataType Block = ViewDataType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Block');
|
||||
static const ViewDataType Grid = ViewDataType._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Grid');
|
||||
|
||||
static const $core.List<ViewDataType> values = <ViewDataType> [
|
||||
RichText,
|
||||
PlainText,
|
||||
Block,
|
||||
Grid,
|
||||
];
|
||||
|
||||
|
@ -12,14 +12,13 @@ import 'dart:typed_data' as $typed_data;
|
||||
const ViewDataType$json = const {
|
||||
'1': 'ViewDataType',
|
||||
'2': const [
|
||||
const {'1': 'RichText', '2': 0},
|
||||
const {'1': 'PlainText', '2': 1},
|
||||
const {'1': 'Grid', '2': 2},
|
||||
const {'1': 'Block', '2': 0},
|
||||
const {'1': 'Grid', '2': 1},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `ViewDataType`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||
final $typed_data.Uint8List viewDataTypeDescriptor = $convert.base64Decode('CgxWaWV3RGF0YVR5cGUSDAoIUmljaFRleHQQABINCglQbGFpblRleHQQARIICgRHcmlkEAI=');
|
||||
final $typed_data.Uint8List viewDataTypeDescriptor = $convert.base64Decode('CgxWaWV3RGF0YVR5cGUSCQoFQmxvY2sQABIICgRHcmlkEAE=');
|
||||
@$core.Deprecated('Use viewDescriptor instead')
|
||||
const View$json = const {
|
||||
'1': 'View',
|
||||
|
@ -26,7 +26,7 @@ class FolderEvent extends $pb.ProtobufEnum {
|
||||
static const FolderEvent DeleteView = FolderEvent._(204, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DeleteView');
|
||||
static const FolderEvent DuplicateView = FolderEvent._(205, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DuplicateView');
|
||||
static const FolderEvent CopyLink = FolderEvent._(206, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CopyLink');
|
||||
static const FolderEvent OpenView = FolderEvent._(207, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'OpenView');
|
||||
static const FolderEvent SetLatestView = FolderEvent._(207, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SetLatestView');
|
||||
static const FolderEvent CloseView = FolderEvent._(208, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CloseView');
|
||||
static const FolderEvent ReadTrash = FolderEvent._(300, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ReadTrash');
|
||||
static const FolderEvent PutbackTrash = FolderEvent._(301, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'PutbackTrash');
|
||||
@ -53,7 +53,7 @@ class FolderEvent extends $pb.ProtobufEnum {
|
||||
DeleteView,
|
||||
DuplicateView,
|
||||
CopyLink,
|
||||
OpenView,
|
||||
SetLatestView,
|
||||
CloseView,
|
||||
ReadTrash,
|
||||
PutbackTrash,
|
||||
|
@ -28,7 +28,7 @@ const FolderEvent$json = const {
|
||||
const {'1': 'DeleteView', '2': 204},
|
||||
const {'1': 'DuplicateView', '2': 205},
|
||||
const {'1': 'CopyLink', '2': 206},
|
||||
const {'1': 'OpenView', '2': 207},
|
||||
const {'1': 'SetLatestView', '2': 207},
|
||||
const {'1': 'CloseView', '2': 208},
|
||||
const {'1': 'ReadTrash', '2': 300},
|
||||
const {'1': 'PutbackTrash', '2': 301},
|
||||
@ -41,4 +41,4 @@ const FolderEvent$json = const {
|
||||
};
|
||||
|
||||
/// Descriptor for `FolderEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||
final $typed_data.Uint8List folderEventDescriptor = $convert.base64Decode('CgtGb2xkZXJFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIUChBSZWFkQ3VyV29ya3NwYWNlEAESEgoOUmVhZFdvcmtzcGFjZXMQAhITCg9EZWxldGVXb3Jrc3BhY2UQAxIRCg1PcGVuV29ya3NwYWNlEAQSFQoRUmVhZFdvcmtzcGFjZUFwcHMQBRINCglDcmVhdGVBcHAQZRINCglEZWxldGVBcHAQZhILCgdSZWFkQXBwEGcSDQoJVXBkYXRlQXBwEGgSDwoKQ3JlYXRlVmlldxDJARINCghSZWFkVmlldxDKARIPCgpVcGRhdGVWaWV3EMsBEg8KCkRlbGV0ZVZpZXcQzAESEgoNRHVwbGljYXRlVmlldxDNARINCghDb3B5TGluaxDOARINCghPcGVuVmlldxDPARIOCglDbG9zZVZpZXcQ0AESDgoJUmVhZFRyYXNoEKwCEhEKDFB1dGJhY2tUcmFzaBCtAhIQCgtEZWxldGVUcmFzaBCuAhIUCg9SZXN0b3JlQWxsVHJhc2gQrwISEwoORGVsZXRlQWxsVHJhc2gQsAISEgoNQXBwbHlEb2NEZWx0YRCQAxITCg5FeHBvcnREb2N1bWVudBD0Aw==');
|
||||
final $typed_data.Uint8List folderEventDescriptor = $convert.base64Decode('CgtGb2xkZXJFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIUChBSZWFkQ3VyV29ya3NwYWNlEAESEgoOUmVhZFdvcmtzcGFjZXMQAhITCg9EZWxldGVXb3Jrc3BhY2UQAxIRCg1PcGVuV29ya3NwYWNlEAQSFQoRUmVhZFdvcmtzcGFjZUFwcHMQBRINCglDcmVhdGVBcHAQZRINCglEZWxldGVBcHAQZhILCgdSZWFkQXBwEGcSDQoJVXBkYXRlQXBwEGgSDwoKQ3JlYXRlVmlldxDJARINCghSZWFkVmlldxDKARIPCgpVcGRhdGVWaWV3EMsBEg8KCkRlbGV0ZVZpZXcQzAESEgoNRHVwbGljYXRlVmlldxDNARINCghDb3B5TGluaxDOARISCg1TZXRMYXRlc3RWaWV3EM8BEg4KCUNsb3NlVmlldxDQARIOCglSZWFkVHJhc2gQrAISEQoMUHV0YmFja1RyYXNoEK0CEhAKC0RlbGV0ZVRyYXNoEK4CEhQKD1Jlc3RvcmVBbGxUcmFzaBCvAhITCg5EZWxldGVBbGxUcmFzaBCwAhISCg1BcHBseURvY0RlbHRhEJADEhMKDkV4cG9ydERvY3VtZW50EPQD');
|
||||
|
@ -10,13 +10,13 @@ import 'dart:core' as $core;
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
class GridEvent extends $pb.ProtobufEnum {
|
||||
static const GridEvent OpenGrid = GridEvent._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'OpenGrid');
|
||||
static const GridEvent GetGridData = GridEvent._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetGridData');
|
||||
static const GridEvent GetRows = GridEvent._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetRows');
|
||||
static const GridEvent GetFields = GridEvent._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetFields');
|
||||
static const GridEvent CreateRow = GridEvent._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateRow');
|
||||
|
||||
static const $core.List<GridEvent> values = <GridEvent> [
|
||||
OpenGrid,
|
||||
GetGridData,
|
||||
GetRows,
|
||||
GetFields,
|
||||
CreateRow,
|
||||
|
@ -12,7 +12,7 @@ import 'dart:typed_data' as $typed_data;
|
||||
const GridEvent$json = const {
|
||||
'1': 'GridEvent',
|
||||
'2': const [
|
||||
const {'1': 'OpenGrid', '2': 0},
|
||||
const {'1': 'GetGridData', '2': 0},
|
||||
const {'1': 'GetRows', '2': 1},
|
||||
const {'1': 'GetFields', '2': 2},
|
||||
const {'1': 'CreateRow', '2': 3},
|
||||
@ -20,4 +20,4 @@ const GridEvent$json = const {
|
||||
};
|
||||
|
||||
/// Descriptor for `GridEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||
final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDAoIT3BlbkdyaWQQABILCgdHZXRSb3dzEAESDQoJR2V0RmllbGRzEAISDQoJQ3JlYXRlUm93EAM=');
|
||||
final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABILCgdHZXRSb3dzEAESDQoJR2V0RmllbGRzEAISDQoJQ3JlYXRlUm93EAM=');
|
||||
|
@ -7,8 +7,8 @@ edition = "2018"
|
||||
[lib]
|
||||
name = "dart_ffi"
|
||||
# this value will change depending on the target os
|
||||
# default staticlib
|
||||
crate-type = ["staticlib"]
|
||||
# default cdylib
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
|
@ -1,11 +1,24 @@
|
||||
use crate::entities::{ExportData, ExportParams, ExportPayload};
|
||||
use crate::BlockManager;
|
||||
use flowy_collaboration::entities::document_info::BlockDelta;
|
||||
use flowy_collaboration::entities::document_info::{BlockDelta, BlockId};
|
||||
use flowy_error::FlowyError;
|
||||
use lib_dispatch::prelude::{data_result, AppData, Data, DataResult};
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub(crate) async fn get_block_data_handler(
|
||||
data: Data<BlockId>,
|
||||
manager: AppData<Arc<BlockManager>>,
|
||||
) -> DataResult<BlockDelta, FlowyError> {
|
||||
let block_id: BlockId = data.into_inner();
|
||||
let editor = manager.open_block(&block_id).await?;
|
||||
let delta_str = editor.delta_str().await?;
|
||||
data_result(BlockDelta {
|
||||
block_id: block_id.into(),
|
||||
delta_str,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn apply_delta_handler(
|
||||
data: Data<BlockDelta>,
|
||||
manager: AppData<Arc<BlockManager>>,
|
||||
|
@ -9,7 +9,8 @@ pub fn create(block_manager: Arc<BlockManager>) -> Module {
|
||||
let mut module = Module::new().name(env!("CARGO_PKG_NAME")).data(block_manager);
|
||||
|
||||
module = module
|
||||
.event(BlockEvent::ApplyDocDelta, apply_delta_handler)
|
||||
.event(BlockEvent::GetBlockData, get_block_data_handler)
|
||||
.event(BlockEvent::ApplyDelta, apply_delta_handler)
|
||||
.event(BlockEvent::ExportDocument, export_handler);
|
||||
|
||||
module
|
||||
@ -18,9 +19,12 @@ pub fn create(block_manager: Arc<BlockManager>) -> Module {
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
|
||||
#[event_err = "FlowyError"]
|
||||
pub enum BlockEvent {
|
||||
#[event(input = "BlockId", output = "BlockDelta")]
|
||||
GetBlockData = 0,
|
||||
|
||||
#[event(input = "BlockDelta", output = "BlockDelta")]
|
||||
ApplyDocDelta = 0,
|
||||
ApplyDelta = 1,
|
||||
|
||||
#[event(input = "ExportPayload", output = "ExportData")]
|
||||
ExportDocument = 1,
|
||||
ExportDocument = 2,
|
||||
}
|
||||
|
@ -25,8 +25,9 @@
|
||||
|
||||
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
|
||||
pub enum BlockEvent {
|
||||
ApplyDocDelta = 0,
|
||||
ExportDocument = 1,
|
||||
GetBlockData = 0,
|
||||
ApplyDelta = 1,
|
||||
ExportDocument = 2,
|
||||
}
|
||||
|
||||
impl ::protobuf::ProtobufEnum for BlockEvent {
|
||||
@ -36,15 +37,17 @@ impl ::protobuf::ProtobufEnum for BlockEvent {
|
||||
|
||||
fn from_i32(value: i32) -> ::std::option::Option<BlockEvent> {
|
||||
match value {
|
||||
0 => ::std::option::Option::Some(BlockEvent::ApplyDocDelta),
|
||||
1 => ::std::option::Option::Some(BlockEvent::ExportDocument),
|
||||
0 => ::std::option::Option::Some(BlockEvent::GetBlockData),
|
||||
1 => ::std::option::Option::Some(BlockEvent::ApplyDelta),
|
||||
2 => ::std::option::Option::Some(BlockEvent::ExportDocument),
|
||||
_ => ::std::option::Option::None
|
||||
}
|
||||
}
|
||||
|
||||
fn values() -> &'static [Self] {
|
||||
static values: &'static [BlockEvent] = &[
|
||||
BlockEvent::ApplyDocDelta,
|
||||
BlockEvent::GetBlockData,
|
||||
BlockEvent::ApplyDelta,
|
||||
BlockEvent::ExportDocument,
|
||||
];
|
||||
values
|
||||
@ -63,7 +66,7 @@ impl ::std::marker::Copy for BlockEvent {
|
||||
|
||||
impl ::std::default::Default for BlockEvent {
|
||||
fn default() -> Self {
|
||||
BlockEvent::ApplyDocDelta
|
||||
BlockEvent::GetBlockData
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,8 +77,9 @@ impl ::protobuf::reflect::ProtobufValue for BlockEvent {
|
||||
}
|
||||
|
||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\n\x0fevent_map.proto*3\n\nBlockEvent\x12\x11\n\rApplyDocDelta\x10\0\x12\
|
||||
\x12\n\x0eExportDocument\x10\x01b\x06proto3\
|
||||
\n\x0fevent_map.proto*B\n\nBlockEvent\x12\x10\n\x0cGetBlockData\x10\0\
|
||||
\x12\x0e\n\nApplyDelta\x10\x01\x12\x12\n\x0eExportDocument\x10\x02b\x06p\
|
||||
roto3\
|
||||
";
|
||||
|
||||
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
|
||||
|
@ -1,6 +1,7 @@
|
||||
syntax = "proto3";
|
||||
|
||||
enum BlockEvent {
|
||||
ApplyDocDelta = 0;
|
||||
ExportDocument = 1;
|
||||
GetBlockData = 0;
|
||||
ApplyDelta = 1;
|
||||
ExportDocument = 2;
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ pub fn create(folder: Arc<FolderManager>) -> Module {
|
||||
.event(FolderEvent::UpdateView, update_view_handler)
|
||||
.event(FolderEvent::DeleteView, delete_view_handler)
|
||||
.event(FolderEvent::DuplicateView, duplicate_view_handler)
|
||||
.event(FolderEvent::OpenView, open_view_handler)
|
||||
.event(FolderEvent::SetLatestView, set_latest_view_handler)
|
||||
.event(FolderEvent::CloseView, close_view_handler);
|
||||
|
||||
module = module
|
||||
@ -127,8 +127,8 @@ pub enum FolderEvent {
|
||||
#[event()]
|
||||
CopyLink = 206,
|
||||
|
||||
#[event(input = "ViewId", output = "BlockDelta")]
|
||||
OpenView = 207,
|
||||
#[event(input = "ViewId")]
|
||||
SetLatestView = 207,
|
||||
|
||||
#[event(input = "ViewId")]
|
||||
CloseView = 208,
|
||||
|
@ -206,9 +206,9 @@ impl DefaultFolderBuilder {
|
||||
} else {
|
||||
initial_quill_delta_string()
|
||||
};
|
||||
view_controller.set_latest_view(view);
|
||||
let _ = view_controller.set_latest_view(&view.id);
|
||||
let _ = view_controller
|
||||
.create_view(&view.id, ViewDataType::RichText, Bytes::from(view_data))
|
||||
.create_view(&view.id, ViewDataType::Block, Bytes::from(view_data))
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ pub enum FolderEvent {
|
||||
DeleteView = 204,
|
||||
DuplicateView = 205,
|
||||
CopyLink = 206,
|
||||
OpenView = 207,
|
||||
SetLatestView = 207,
|
||||
CloseView = 208,
|
||||
ReadTrash = 300,
|
||||
PutbackTrash = 301,
|
||||
@ -75,7 +75,7 @@ impl ::protobuf::ProtobufEnum for FolderEvent {
|
||||
204 => ::std::option::Option::Some(FolderEvent::DeleteView),
|
||||
205 => ::std::option::Option::Some(FolderEvent::DuplicateView),
|
||||
206 => ::std::option::Option::Some(FolderEvent::CopyLink),
|
||||
207 => ::std::option::Option::Some(FolderEvent::OpenView),
|
||||
207 => ::std::option::Option::Some(FolderEvent::SetLatestView),
|
||||
208 => ::std::option::Option::Some(FolderEvent::CloseView),
|
||||
300 => ::std::option::Option::Some(FolderEvent::ReadTrash),
|
||||
301 => ::std::option::Option::Some(FolderEvent::PutbackTrash),
|
||||
@ -106,7 +106,7 @@ impl ::protobuf::ProtobufEnum for FolderEvent {
|
||||
FolderEvent::DeleteView,
|
||||
FolderEvent::DuplicateView,
|
||||
FolderEvent::CopyLink,
|
||||
FolderEvent::OpenView,
|
||||
FolderEvent::SetLatestView,
|
||||
FolderEvent::CloseView,
|
||||
FolderEvent::ReadTrash,
|
||||
FolderEvent::PutbackTrash,
|
||||
@ -143,19 +143,19 @@ impl ::protobuf::reflect::ProtobufValue for FolderEvent {
|
||||
}
|
||||
|
||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\n\x0fevent_map.proto*\xd2\x03\n\x0bFolderEvent\x12\x13\n\x0fCreateWorks\
|
||||
\n\x0fevent_map.proto*\xd7\x03\n\x0bFolderEvent\x12\x13\n\x0fCreateWorks\
|
||||
pace\x10\0\x12\x14\n\x10ReadCurWorkspace\x10\x01\x12\x12\n\x0eReadWorksp\
|
||||
aces\x10\x02\x12\x13\n\x0fDeleteWorkspace\x10\x03\x12\x11\n\rOpenWorkspa\
|
||||
ce\x10\x04\x12\x15\n\x11ReadWorkspaceApps\x10\x05\x12\r\n\tCreateApp\x10\
|
||||
e\x12\r\n\tDeleteApp\x10f\x12\x0b\n\x07ReadApp\x10g\x12\r\n\tUpdateApp\
|
||||
\x10h\x12\x0f\n\nCreateView\x10\xc9\x01\x12\r\n\x08ReadView\x10\xca\x01\
|
||||
\x12\x0f\n\nUpdateView\x10\xcb\x01\x12\x0f\n\nDeleteView\x10\xcc\x01\x12\
|
||||
\x12\n\rDuplicateView\x10\xcd\x01\x12\r\n\x08CopyLink\x10\xce\x01\x12\r\
|
||||
\n\x08OpenView\x10\xcf\x01\x12\x0e\n\tCloseView\x10\xd0\x01\x12\x0e\n\tR\
|
||||
eadTrash\x10\xac\x02\x12\x11\n\x0cPutbackTrash\x10\xad\x02\x12\x10\n\x0b\
|
||||
DeleteTrash\x10\xae\x02\x12\x14\n\x0fRestoreAllTrash\x10\xaf\x02\x12\x13\
|
||||
\n\x0eDeleteAllTrash\x10\xb0\x02\x12\x12\n\rApplyDocDelta\x10\x90\x03\
|
||||
\x12\x13\n\x0eExportDocument\x10\xf4\x03b\x06proto3\
|
||||
\x12\n\rDuplicateView\x10\xcd\x01\x12\r\n\x08CopyLink\x10\xce\x01\x12\
|
||||
\x12\n\rSetLatestView\x10\xcf\x01\x12\x0e\n\tCloseView\x10\xd0\x01\x12\
|
||||
\x0e\n\tReadTrash\x10\xac\x02\x12\x11\n\x0cPutbackTrash\x10\xad\x02\x12\
|
||||
\x10\n\x0bDeleteTrash\x10\xae\x02\x12\x14\n\x0fRestoreAllTrash\x10\xaf\
|
||||
\x02\x12\x13\n\x0eDeleteAllTrash\x10\xb0\x02\x12\x12\n\rApplyDocDelta\
|
||||
\x10\x90\x03\x12\x13\n\x0eExportDocument\x10\xf4\x03b\x06proto3\
|
||||
";
|
||||
|
||||
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
|
||||
|
@ -17,7 +17,7 @@ enum FolderEvent {
|
||||
DeleteView = 204;
|
||||
DuplicateView = 205;
|
||||
CopyLink = 206;
|
||||
OpenView = 207;
|
||||
SetLatestView = 207;
|
||||
CloseView = 208;
|
||||
ReadTrash = 300;
|
||||
PutbackTrash = 301;
|
||||
|
@ -84,8 +84,7 @@ pub(crate) struct ViewTable {
|
||||
impl ViewTable {
|
||||
pub fn new(view: View) -> Self {
|
||||
let data_type = match view.data_type {
|
||||
ViewDataType::RichText => SqlViewDataType::RichText,
|
||||
ViewDataType::PlainText => SqlViewDataType::PlainText,
|
||||
ViewDataType::Block => SqlViewDataType::Block,
|
||||
ViewDataType::Grid => SqlViewDataType::Grid,
|
||||
};
|
||||
|
||||
@ -107,8 +106,7 @@ impl ViewTable {
|
||||
impl std::convert::From<ViewTable> for View {
|
||||
fn from(table: ViewTable) -> Self {
|
||||
let data_type = match table.view_type {
|
||||
SqlViewDataType::RichText => ViewDataType::RichText,
|
||||
SqlViewDataType::PlainText => ViewDataType::PlainText,
|
||||
SqlViewDataType::Block => ViewDataType::Block,
|
||||
SqlViewDataType::Grid => ViewDataType::Grid,
|
||||
};
|
||||
|
||||
@ -179,26 +177,24 @@ impl ViewChangeset {
|
||||
#[repr(i32)]
|
||||
#[sql_type = "Integer"]
|
||||
pub enum SqlViewDataType {
|
||||
RichText = 0,
|
||||
PlainText = 1,
|
||||
Grid = 2,
|
||||
Block = 0,
|
||||
Grid = 1,
|
||||
}
|
||||
|
||||
impl std::default::Default for SqlViewDataType {
|
||||
fn default() -> Self {
|
||||
SqlViewDataType::RichText
|
||||
SqlViewDataType::Block
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<i32> for SqlViewDataType {
|
||||
fn from(value: i32) -> Self {
|
||||
match value {
|
||||
0 => SqlViewDataType::RichText,
|
||||
1 => SqlViewDataType::PlainText,
|
||||
2 => SqlViewDataType::Grid,
|
||||
0 => SqlViewDataType::Block,
|
||||
1 => SqlViewDataType::Grid,
|
||||
o => {
|
||||
log::error!("Unsupported view type {}, fallback to ViewType::Docs", o);
|
||||
SqlViewDataType::PlainText
|
||||
log::error!("Unsupported view type {}, fallback to ViewType::Block", o);
|
||||
SqlViewDataType::Block
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ use crate::{
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use flowy_collaboration::entities::{
|
||||
document_info::{BlockDelta, BlockId},
|
||||
document_info::BlockId,
|
||||
revision::{RepeatedRevision, Revision},
|
||||
};
|
||||
use flowy_database::kv::KV;
|
||||
@ -134,14 +134,9 @@ impl ViewController {
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self), err)]
|
||||
pub(crate) async fn open_view(&self, view_id: &str) -> Result<BlockDelta, FlowyError> {
|
||||
let processor = self.get_data_processor_from_view_id(view_id).await?;
|
||||
let delta_str = processor.delta_str(view_id).await?;
|
||||
pub(crate) fn set_latest_view(&self, view_id: &str) -> Result<(), FlowyError> {
|
||||
KV::set_str(LATEST_VIEW_ID, view_id.to_owned());
|
||||
Ok(BlockDelta {
|
||||
block_id: view_id.to_string(),
|
||||
delta_str,
|
||||
})
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self), err)]
|
||||
@ -231,10 +226,6 @@ impl ViewController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_latest_view(&self, view: &View) {
|
||||
KV::set_str(LATEST_VIEW_ID, view.id.clone());
|
||||
}
|
||||
}
|
||||
|
||||
impl ViewController {
|
||||
|
@ -8,7 +8,6 @@ use crate::{
|
||||
errors::FlowyError,
|
||||
services::{TrashController, ViewController},
|
||||
};
|
||||
use flowy_collaboration::entities::document_info::BlockDelta;
|
||||
use lib_dispatch::prelude::{data_result, AppData, Data, DataResult};
|
||||
use std::{convert::TryInto, sync::Arc};
|
||||
|
||||
@ -66,13 +65,13 @@ pub(crate) async fn delete_view_handler(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn open_view_handler(
|
||||
pub(crate) async fn set_latest_view_handler(
|
||||
data: Data<ViewId>,
|
||||
controller: AppData<Arc<ViewController>>,
|
||||
) -> DataResult<BlockDelta, FlowyError> {
|
||||
) -> Result<(), FlowyError> {
|
||||
let view_id: ViewId = data.into_inner();
|
||||
let doc = controller.open_view(&view_id.value).await?;
|
||||
data_result(doc)
|
||||
let _ = controller.set_latest_view(&view_id.value)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn close_view_handler(
|
||||
|
@ -164,7 +164,7 @@ pub async fn delete_view(sdk: &FlowySDKTest, view_ids: Vec<String>) {
|
||||
pub async fn open_document(sdk: &FlowySDKTest, view_id: &str) -> BlockInfo {
|
||||
let view_id: ViewId = view_id.into();
|
||||
FolderEventBuilder::new(sdk.clone())
|
||||
.event(OpenView)
|
||||
.event(SetLatestView)
|
||||
.payload(view_id)
|
||||
.async_send()
|
||||
.await
|
||||
|
@ -68,7 +68,7 @@ impl FolderTest {
|
||||
let _ = sdk.init_user().await;
|
||||
let mut workspace = create_workspace(&sdk, "FolderWorkspace", "Folder test workspace").await;
|
||||
let mut app = create_app(&sdk, &workspace.id, "Folder App", "Folder test app").await;
|
||||
let view = create_view(&sdk, &app.id, "Folder View", "Folder test view", ViewDataType::RichText).await;
|
||||
let view = create_view(&sdk, &app.id, "Folder View", "Folder test view", ViewDataType::Block).await;
|
||||
app.belongings = RepeatedView {
|
||||
items: vec![view.clone()],
|
||||
};
|
||||
@ -146,7 +146,7 @@ impl FolderTest {
|
||||
}
|
||||
|
||||
FolderScript::CreateView { name, desc } => {
|
||||
let view = create_view(sdk, &self.app.id, &name, &desc, ViewDataType::RichText).await;
|
||||
let view = create_view(sdk, &self.app.id, &name, &desc, ViewDataType::Block).await;
|
||||
self.view = view;
|
||||
}
|
||||
FolderScript::AssertView(view) => {
|
||||
|
@ -5,7 +5,7 @@ use lib_dispatch::prelude::{data_result, AppData, Data, DataResult};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
pub(crate) async fn open_grid_handler(
|
||||
pub(crate) async fn get_grid_data_handler(
|
||||
data: Data<GridId>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
) -> DataResult<Grid, FlowyError> {
|
||||
|
@ -8,7 +8,7 @@ use strum_macros::Display;
|
||||
pub fn create(grid_manager: Arc<GridManager>) -> Module {
|
||||
let mut module = Module::new().name(env!("CARGO_PKG_NAME")).data(grid_manager);
|
||||
module = module
|
||||
.event(GridEvent::OpenGrid, open_grid_handler)
|
||||
.event(GridEvent::GetGridData, get_grid_data_handler)
|
||||
.event(GridEvent::GetRows, get_rows_handler)
|
||||
.event(GridEvent::GetFields, get_fields_handler)
|
||||
.event(GridEvent::CreateRow, create_row_handler);
|
||||
@ -20,7 +20,7 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
|
||||
#[event_err = "FlowyError"]
|
||||
pub enum GridEvent {
|
||||
#[event(input = "GridId", output = "Grid")]
|
||||
OpenGrid = 0,
|
||||
GetGridData = 0,
|
||||
|
||||
#[event(input = "QueryRowPayload", output = "RepeatedRow")]
|
||||
GetRows = 1,
|
||||
|
@ -6,4 +6,4 @@ pub mod event_map;
|
||||
pub mod manager;
|
||||
|
||||
mod protobuf;
|
||||
mod services;
|
||||
pub mod services;
|
||||
|
@ -1,11 +1,12 @@
|
||||
use crate::services::grid_editor::ClientGridEditor;
|
||||
use crate::services::kv_persistence::GridKVPersistence;
|
||||
use crate::services::kv_persistence::{GridKVPersistence, KVTransaction};
|
||||
use dashmap::DashMap;
|
||||
use flowy_collaboration::client_grid::make_grid_delta;
|
||||
|
||||
use flowy_collaboration::entities::revision::RepeatedRevision;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::entities::{Field, FieldOrder, FieldType, Grid, RawRow, RowOrder};
|
||||
use flowy_grid_data_model::entities::{Field, RawRow};
|
||||
use flowy_sync::{RevisionManager, RevisionPersistence, RevisionWebSocket};
|
||||
|
||||
use lib_sqlite::ConnectionPool;
|
||||
use parking_lot::RwLock;
|
||||
use std::sync::Arc;
|
||||
@ -76,6 +77,18 @@ impl GridManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save_rows(&self, rows: Vec<RawRow>) -> FlowyResult<()> {
|
||||
let kv_persistence = self.get_kv_persistence()?;
|
||||
let _ = kv_persistence.batch_set(rows)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn save_fields(&self, fields: Vec<Field>) -> FlowyResult<()> {
|
||||
let kv_persistence = self.get_kv_persistence()?;
|
||||
let _ = kv_persistence.batch_set(fields)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_or_create_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<ClientGridEditor>> {
|
||||
match self.editor_map.get(grid_id) {
|
||||
None => {
|
||||
@ -122,58 +135,6 @@ impl GridManager {
|
||||
}
|
||||
}
|
||||
|
||||
use lib_infra::uuid;
|
||||
pub fn default_grid(grid_id: &str) -> String {
|
||||
let fields = vec![
|
||||
Field {
|
||||
id: uuid(),
|
||||
name: "".to_string(),
|
||||
desc: "".to_string(),
|
||||
field_type: FieldType::RichText,
|
||||
frozen: false,
|
||||
width: 100,
|
||||
type_options: Default::default(),
|
||||
},
|
||||
Field {
|
||||
id: uuid(),
|
||||
name: "".to_string(),
|
||||
desc: "".to_string(),
|
||||
field_type: FieldType::RichText,
|
||||
frozen: false,
|
||||
width: 100,
|
||||
type_options: Default::default(),
|
||||
},
|
||||
];
|
||||
|
||||
let rows = vec![
|
||||
RawRow {
|
||||
id: uuid(),
|
||||
grid_id: grid_id.to_string(),
|
||||
cell_by_field_id: Default::default(),
|
||||
},
|
||||
RawRow {
|
||||
id: uuid(),
|
||||
grid_id: grid_id.to_string(),
|
||||
cell_by_field_id: Default::default(),
|
||||
},
|
||||
];
|
||||
|
||||
make_grid(&grid_id, fields, rows)
|
||||
}
|
||||
|
||||
pub fn make_grid(grid_id: &str, fields: Vec<Field>, rows: Vec<RawRow>) -> String {
|
||||
let field_orders = fields.iter().map(FieldOrder::from).collect::<Vec<_>>();
|
||||
let row_orders = rows.iter().map(RowOrder::from).collect::<Vec<_>>();
|
||||
|
||||
let grid = Grid {
|
||||
id: grid_id.to_owned(),
|
||||
field_orders: field_orders.into(),
|
||||
row_orders: row_orders.into(),
|
||||
};
|
||||
let delta = make_grid_delta(&grid);
|
||||
delta.to_delta_str()
|
||||
}
|
||||
|
||||
pub struct GridEditorMap {
|
||||
inner: DashMap<String, Arc<ClientGridEditor>>,
|
||||
}
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
|
||||
pub enum GridEvent {
|
||||
OpenGrid = 0,
|
||||
GetGridData = 0,
|
||||
GetRows = 1,
|
||||
GetFields = 2,
|
||||
CreateRow = 3,
|
||||
@ -38,7 +38,7 @@ impl ::protobuf::ProtobufEnum for GridEvent {
|
||||
|
||||
fn from_i32(value: i32) -> ::std::option::Option<GridEvent> {
|
||||
match value {
|
||||
0 => ::std::option::Option::Some(GridEvent::OpenGrid),
|
||||
0 => ::std::option::Option::Some(GridEvent::GetGridData),
|
||||
1 => ::std::option::Option::Some(GridEvent::GetRows),
|
||||
2 => ::std::option::Option::Some(GridEvent::GetFields),
|
||||
3 => ::std::option::Option::Some(GridEvent::CreateRow),
|
||||
@ -48,7 +48,7 @@ impl ::protobuf::ProtobufEnum for GridEvent {
|
||||
|
||||
fn values() -> &'static [Self] {
|
||||
static values: &'static [GridEvent] = &[
|
||||
GridEvent::OpenGrid,
|
||||
GridEvent::GetGridData,
|
||||
GridEvent::GetRows,
|
||||
GridEvent::GetFields,
|
||||
GridEvent::CreateRow,
|
||||
@ -69,7 +69,7 @@ impl ::std::marker::Copy for GridEvent {
|
||||
|
||||
impl ::std::default::Default for GridEvent {
|
||||
fn default() -> Self {
|
||||
GridEvent::OpenGrid
|
||||
GridEvent::GetGridData
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,9 +80,9 @@ impl ::protobuf::reflect::ProtobufValue for GridEvent {
|
||||
}
|
||||
|
||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\n\x0fevent_map.proto*D\n\tGridEvent\x12\x0c\n\x08OpenGrid\x10\0\x12\x0b\
|
||||
\n\x07GetRows\x10\x01\x12\r\n\tGetFields\x10\x02\x12\r\n\tCreateRow\x10\
|
||||
\x03b\x06proto3\
|
||||
\n\x0fevent_map.proto*G\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\0\x12\
|
||||
\x0b\n\x07GetRows\x10\x01\x12\r\n\tGetFields\x10\x02\x12\r\n\tCreateRow\
|
||||
\x10\x03b\x06proto3\
|
||||
";
|
||||
|
||||
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
|
||||
|
@ -1,7 +1,7 @@
|
||||
syntax = "proto3";
|
||||
|
||||
enum GridEvent {
|
||||
OpenGrid = 0;
|
||||
GetGridData = 0;
|
||||
GetRows = 1;
|
||||
GetFields = 2;
|
||||
CreateRow = 3;
|
||||
|
@ -414,7 +414,7 @@ impl std::default::Default for FlowyMoney {
|
||||
|
||||
impl FlowyMoney {
|
||||
// Currency list https://docs.rs/rusty-money/0.4.0/rusty_money/iso/index.html
|
||||
pub fn from_str(s: &str) -> FlowyMoney {
|
||||
pub fn from_symbol_str(s: &str) -> FlowyMoney {
|
||||
match s {
|
||||
"CNY" => FlowyMoney::CNY,
|
||||
"EUR" => FlowyMoney::EUR,
|
||||
@ -424,7 +424,7 @@ impl FlowyMoney {
|
||||
}
|
||||
|
||||
pub fn from_money(money: &rusty_money::Money<Currency>) -> FlowyMoney {
|
||||
FlowyMoney::from_str(&money.currency().symbol.to_string())
|
||||
FlowyMoney::from_symbol_str(&money.currency().symbol.to_string())
|
||||
}
|
||||
|
||||
pub fn currency(&self) -> &'static Currency {
|
||||
|
106
frontend/rust-lib/flowy-grid/src/services/grid_builder.rs
Normal file
106
frontend/rust-lib/flowy-grid/src/services/grid_builder.rs
Normal file
@ -0,0 +1,106 @@
|
||||
use crate::manager::GridManager;
|
||||
use flowy_collaboration::client_grid::make_grid_delta;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::entities::{Field, FieldOrder, FieldType, Grid, RawCell, RawRow, RowOrder};
|
||||
use lib_infra::uuid;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct GridBuilder {
|
||||
grid_manager: Arc<GridManager>,
|
||||
grid_id: String,
|
||||
fields: Vec<Field>,
|
||||
rows: Vec<RawRow>,
|
||||
}
|
||||
|
||||
impl GridBuilder {
|
||||
pub fn new(grid_id: &str, grid_manager: Arc<GridManager>) -> Self {
|
||||
Self {
|
||||
grid_manager,
|
||||
grid_id: grid_id.to_owned(),
|
||||
fields: vec![],
|
||||
rows: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_field(mut self, name: &str, desc: &str, field_type: FieldType) -> Self {
|
||||
let field = Field {
|
||||
id: uuid(),
|
||||
name: name.to_string(),
|
||||
desc: desc.to_string(),
|
||||
field_type,
|
||||
frozen: false,
|
||||
width: 100,
|
||||
type_options: Default::default(),
|
||||
};
|
||||
self.fields.push(field);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_empty_row(mut self) -> Self {
|
||||
let row = RawRow {
|
||||
id: uuid(),
|
||||
grid_id: self.grid_id.clone(),
|
||||
cell_by_field_id: Default::default(),
|
||||
};
|
||||
self.rows.push(row);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_row(mut self, cells: Vec<RawCell>) -> Self {
|
||||
let cell_by_field_id = cells
|
||||
.into_iter()
|
||||
.map(|cell| (cell.id.clone(), cell))
|
||||
.collect::<HashMap<String, RawCell>>();
|
||||
|
||||
let row = RawRow {
|
||||
id: uuid(),
|
||||
grid_id: self.grid_id.clone(),
|
||||
cell_by_field_id,
|
||||
};
|
||||
self.rows.push(row);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> FlowyResult<String> {
|
||||
let field_orders = self.fields.iter().map(FieldOrder::from).collect::<Vec<FieldOrder>>();
|
||||
|
||||
let row_orders = self.rows.iter().map(RowOrder::from).collect::<Vec<RowOrder>>();
|
||||
|
||||
let grid = Grid {
|
||||
id: self.grid_id,
|
||||
field_orders: field_orders.into(),
|
||||
row_orders: row_orders.into(),
|
||||
};
|
||||
|
||||
// let _ = check_rows(&self.fields, &self.rows)?;
|
||||
let _ = self.grid_manager.save_rows(self.rows)?;
|
||||
let _ = self.grid_manager.save_fields(self.fields)?;
|
||||
|
||||
let delta = make_grid_delta(&grid);
|
||||
Ok(delta.to_delta_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn check_rows(fields: &[Field], rows: &[RawRow]) -> FlowyResult<()> {
|
||||
let field_ids = fields.iter().map(|field| &field.id).collect::<Vec<&String>>();
|
||||
for row in rows {
|
||||
let cell_field_ids = row.cell_by_field_id.keys().into_iter().collect::<Vec<&String>>();
|
||||
if cell_field_ids != field_ids {
|
||||
let msg = format!("{:?} contains invalid cells", row);
|
||||
return Err(FlowyError::internal().context(msg));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn make_default_grid(grid_id: &str, grid_manager: Arc<GridManager>) -> String {
|
||||
GridBuilder::new(grid_id, grid_manager)
|
||||
.add_field("Name", "", FieldType::RichText)
|
||||
.add_field("Tags", "", FieldType::SingleSelect)
|
||||
.add_empty_row()
|
||||
.add_empty_row()
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
mod util;
|
||||
|
||||
pub mod cell_data;
|
||||
pub mod grid_builder;
|
||||
pub mod grid_editor;
|
||||
pub mod kv_persistence;
|
||||
pub mod stringify;
|
||||
|
@ -25,7 +25,7 @@ fn generate_currency_by_symbol() -> HashMap<String, &'static Currency> {
|
||||
#[allow(dead_code)]
|
||||
pub fn string_to_money(money_str: &str) -> Option<Money<Currency>> {
|
||||
let mut process_money_str = String::from(money_str);
|
||||
let default_currency = FlowyMoney::from_str("CNY").currency();
|
||||
let default_currency = FlowyMoney::from_symbol_str("CNY").currency();
|
||||
|
||||
if process_money_str.is_empty() {
|
||||
return None;
|
||||
|
@ -11,7 +11,8 @@ use flowy_folder::{
|
||||
event_map::{FolderCouldServiceV1, WorkspaceDatabase, WorkspaceUser},
|
||||
manager::FolderManager,
|
||||
};
|
||||
use flowy_grid::manager::{default_grid, GridManager};
|
||||
use flowy_grid::manager::GridManager;
|
||||
use flowy_grid::services::grid_builder::make_default_grid;
|
||||
use flowy_net::ClientServerConfiguration;
|
||||
use flowy_net::{
|
||||
http_server::folder::FolderHttpCloudService, local_server::LocalServer, ws::connection::FlowyWebSocketConnect,
|
||||
@ -178,7 +179,7 @@ impl ViewDataProcessor for BlockManagerViewDataImpl {
|
||||
}
|
||||
|
||||
fn data_type(&self) -> ViewDataType {
|
||||
ViewDataType::RichText
|
||||
ViewDataType::Block
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,7 +227,7 @@ impl ViewDataProcessor for GridManagerViewDataImpl {
|
||||
}
|
||||
|
||||
fn default_view_data(&self, view_id: &str) -> String {
|
||||
default_grid(view_id)
|
||||
make_default_grid(view_id, self.0.clone())
|
||||
}
|
||||
|
||||
fn data_type(&self) -> ViewDataType {
|
||||
|
@ -88,7 +88,7 @@ async fn create_view(sdk: &FlowySDKTest, app_id: &str) -> View {
|
||||
name: "View A".to_string(),
|
||||
desc: "".to_string(),
|
||||
thumbnail: Some("http://1.png".to_string()),
|
||||
data_type: ViewDataType::RichText,
|
||||
data_type: ViewDataType::Block,
|
||||
ext_data: "".to_string(),
|
||||
plugin_type: 0,
|
||||
};
|
||||
|
@ -92,6 +92,11 @@ pub struct BlockId {
|
||||
#[pb(index = 1)]
|
||||
pub value: String,
|
||||
}
|
||||
impl AsRef<str> for BlockId {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<String> for BlockId {
|
||||
fn from(value: String) -> Self {
|
||||
@ -99,10 +104,14 @@ impl std::convert::From<String> for BlockId {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<&String> for BlockId {
|
||||
fn from(doc_id: &String) -> Self {
|
||||
BlockId {
|
||||
value: doc_id.to_owned(),
|
||||
}
|
||||
impl std::convert::From<BlockId> for String {
|
||||
fn from(block_id: BlockId) -> Self {
|
||||
block_id.value
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<&String> for BlockId {
|
||||
fn from(s: &String) -> Self {
|
||||
BlockId { value: s.to_owned() }
|
||||
}
|
||||
}
|
||||
|
@ -83,26 +83,24 @@ impl std::convert::From<View> for Trash {
|
||||
#[derive(Eq, PartialEq, Hash, Debug, ProtoBuf_Enum, Clone, Serialize_repr, Deserialize_repr)]
|
||||
#[repr(u8)]
|
||||
pub enum ViewDataType {
|
||||
RichText = 0,
|
||||
PlainText = 1,
|
||||
Grid = 2,
|
||||
Block = 0,
|
||||
Grid = 1,
|
||||
}
|
||||
|
||||
impl std::default::Default for ViewDataType {
|
||||
fn default() -> Self {
|
||||
ViewDataType::RichText
|
||||
ViewDataType::Block
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<i32> for ViewDataType {
|
||||
fn from(val: i32) -> Self {
|
||||
match val {
|
||||
0 => ViewDataType::RichText,
|
||||
1 => ViewDataType::PlainText,
|
||||
2 => ViewDataType::Grid,
|
||||
0 => ViewDataType::Block,
|
||||
1 => ViewDataType::Grid,
|
||||
_ => {
|
||||
log::error!("Invalid view type: {}", val);
|
||||
ViewDataType::PlainText
|
||||
ViewDataType::Block
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -165,7 +165,7 @@ impl View {
|
||||
self.data_type
|
||||
}
|
||||
pub fn clear_data_type(&mut self) {
|
||||
self.data_type = ViewDataType::RichText;
|
||||
self.data_type = ViewDataType::Block;
|
||||
}
|
||||
|
||||
// Param is passed by value, moved
|
||||
@ -409,7 +409,7 @@ impl ::protobuf::Message for View {
|
||||
if !self.desc.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(4, &self.desc);
|
||||
}
|
||||
if self.data_type != ViewDataType::RichText {
|
||||
if self.data_type != ViewDataType::Block {
|
||||
my_size += ::protobuf::rt::enum_size(5, self.data_type);
|
||||
}
|
||||
if self.version != 0 {
|
||||
@ -452,7 +452,7 @@ impl ::protobuf::Message for View {
|
||||
if !self.desc.is_empty() {
|
||||
os.write_string(4, &self.desc)?;
|
||||
}
|
||||
if self.data_type != ViewDataType::RichText {
|
||||
if self.data_type != ViewDataType::Block {
|
||||
os.write_enum(5, ::protobuf::ProtobufEnum::value(&self.data_type))?;
|
||||
}
|
||||
if self.version != 0 {
|
||||
@ -596,7 +596,7 @@ impl ::protobuf::Clear for View {
|
||||
self.belong_to_id.clear();
|
||||
self.name.clear();
|
||||
self.desc.clear();
|
||||
self.data_type = ViewDataType::RichText;
|
||||
self.data_type = ViewDataType::Block;
|
||||
self.version = 0;
|
||||
self.belongings.clear();
|
||||
self.modified_time = 0;
|
||||
@ -952,7 +952,7 @@ impl CreateViewPayload {
|
||||
self.data_type
|
||||
}
|
||||
pub fn clear_data_type(&mut self) {
|
||||
self.data_type = ViewDataType::RichText;
|
||||
self.data_type = ViewDataType::Block;
|
||||
}
|
||||
|
||||
// Param is passed by value, moved
|
||||
@ -1060,7 +1060,7 @@ impl ::protobuf::Message for CreateViewPayload {
|
||||
if !self.desc.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(3, &self.desc);
|
||||
}
|
||||
if self.data_type != ViewDataType::RichText {
|
||||
if self.data_type != ViewDataType::Block {
|
||||
my_size += ::protobuf::rt::enum_size(5, self.data_type);
|
||||
}
|
||||
if !self.ext_data.is_empty() {
|
||||
@ -1091,7 +1091,7 @@ impl ::protobuf::Message for CreateViewPayload {
|
||||
if !self.desc.is_empty() {
|
||||
os.write_string(3, &self.desc)?;
|
||||
}
|
||||
if self.data_type != ViewDataType::RichText {
|
||||
if self.data_type != ViewDataType::Block {
|
||||
os.write_enum(5, ::protobuf::ProtobufEnum::value(&self.data_type))?;
|
||||
}
|
||||
if !self.ext_data.is_empty() {
|
||||
@ -1200,7 +1200,7 @@ impl ::protobuf::Clear for CreateViewPayload {
|
||||
self.name.clear();
|
||||
self.desc.clear();
|
||||
self.one_of_thumbnail = ::std::option::Option::None;
|
||||
self.data_type = ViewDataType::RichText;
|
||||
self.data_type = ViewDataType::Block;
|
||||
self.ext_data.clear();
|
||||
self.plugin_type = 0;
|
||||
self.unknown_fields.clear();
|
||||
@ -1358,7 +1358,7 @@ impl CreateViewParams {
|
||||
self.data_type
|
||||
}
|
||||
pub fn clear_data_type(&mut self) {
|
||||
self.data_type = ViewDataType::RichText;
|
||||
self.data_type = ViewDataType::Block;
|
||||
}
|
||||
|
||||
// Param is passed by value, moved
|
||||
@ -1524,7 +1524,7 @@ impl ::protobuf::Message for CreateViewParams {
|
||||
if !self.thumbnail.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(4, &self.thumbnail);
|
||||
}
|
||||
if self.data_type != ViewDataType::RichText {
|
||||
if self.data_type != ViewDataType::Block {
|
||||
my_size += ::protobuf::rt::enum_size(5, self.data_type);
|
||||
}
|
||||
if !self.ext_data.is_empty() {
|
||||
@ -1557,7 +1557,7 @@ impl ::protobuf::Message for CreateViewParams {
|
||||
if !self.thumbnail.is_empty() {
|
||||
os.write_string(4, &self.thumbnail)?;
|
||||
}
|
||||
if self.data_type != ViewDataType::RichText {
|
||||
if self.data_type != ViewDataType::Block {
|
||||
os.write_enum(5, ::protobuf::ProtobufEnum::value(&self.data_type))?;
|
||||
}
|
||||
if !self.ext_data.is_empty() {
|
||||
@ -1675,7 +1675,7 @@ impl ::protobuf::Clear for CreateViewParams {
|
||||
self.name.clear();
|
||||
self.desc.clear();
|
||||
self.thumbnail.clear();
|
||||
self.data_type = ViewDataType::RichText;
|
||||
self.data_type = ViewDataType::Block;
|
||||
self.ext_data.clear();
|
||||
self.view_id.clear();
|
||||
self.data.clear();
|
||||
@ -2821,9 +2821,8 @@ impl ::protobuf::reflect::ProtobufValue for UpdateViewParams {
|
||||
|
||||
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
|
||||
pub enum ViewDataType {
|
||||
RichText = 0,
|
||||
PlainText = 1,
|
||||
Grid = 2,
|
||||
Block = 0,
|
||||
Grid = 1,
|
||||
}
|
||||
|
||||
impl ::protobuf::ProtobufEnum for ViewDataType {
|
||||
@ -2833,17 +2832,15 @@ impl ::protobuf::ProtobufEnum for ViewDataType {
|
||||
|
||||
fn from_i32(value: i32) -> ::std::option::Option<ViewDataType> {
|
||||
match value {
|
||||
0 => ::std::option::Option::Some(ViewDataType::RichText),
|
||||
1 => ::std::option::Option::Some(ViewDataType::PlainText),
|
||||
2 => ::std::option::Option::Some(ViewDataType::Grid),
|
||||
0 => ::std::option::Option::Some(ViewDataType::Block),
|
||||
1 => ::std::option::Option::Some(ViewDataType::Grid),
|
||||
_ => ::std::option::Option::None
|
||||
}
|
||||
}
|
||||
|
||||
fn values() -> &'static [Self] {
|
||||
static values: &'static [ViewDataType] = &[
|
||||
ViewDataType::RichText,
|
||||
ViewDataType::PlainText,
|
||||
ViewDataType::Block,
|
||||
ViewDataType::Grid,
|
||||
];
|
||||
values
|
||||
@ -2862,7 +2859,7 @@ impl ::std::marker::Copy for ViewDataType {
|
||||
|
||||
impl ::std::default::Default for ViewDataType {
|
||||
fn default() -> Self {
|
||||
ViewDataType::RichText
|
||||
ViewDataType::Block
|
||||
}
|
||||
}
|
||||
|
||||
@ -2907,9 +2904,8 @@ static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\x17\n\x07view_id\x18\x01\x20\x01(\tR\x06viewId\x12\x14\n\x04name\x18\
|
||||
\x02\x20\x01(\tH\0R\x04name\x12\x14\n\x04desc\x18\x03\x20\x01(\tH\x01R\
|
||||
\x04desc\x12\x1e\n\tthumbnail\x18\x04\x20\x01(\tH\x02R\tthumbnailB\r\n\
|
||||
\x0bone_of_nameB\r\n\x0bone_of_descB\x12\n\x10one_of_thumbnail*5\n\x0cVi\
|
||||
ewDataType\x12\x0c\n\x08RichText\x10\0\x12\r\n\tPlainText\x10\x01\x12\
|
||||
\x08\n\x04Grid\x10\x02b\x06proto3\
|
||||
\x0bone_of_nameB\r\n\x0bone_of_descB\x12\n\x10one_of_thumbnail*#\n\x0cVi\
|
||||
ewDataType\x12\t\n\x05Block\x10\0\x12\x08\n\x04Grid\x10\x01b\x06proto3\
|
||||
";
|
||||
|
||||
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
|
||||
|
@ -56,7 +56,6 @@ message UpdateViewParams {
|
||||
oneof one_of_thumbnail { string thumbnail = 4; };
|
||||
}
|
||||
enum ViewDataType {
|
||||
RichText = 0;
|
||||
PlainText = 1;
|
||||
Grid = 2;
|
||||
Block = 0;
|
||||
Grid = 1;
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ fn create_default_view(app_id: String, time: chrono::DateTime<Utc>) -> View {
|
||||
let view_id = uuid::Uuid::new_v4();
|
||||
let name = "Read me".to_string();
|
||||
let desc = "".to_string();
|
||||
let data_type = ViewDataType::RichText;
|
||||
let data_type = ViewDataType::Block;
|
||||
|
||||
View {
|
||||
id: view_id.to_string(),
|
||||
|
Loading…
Reference in New Issue
Block a user