chore: config notification of grid

This commit is contained in:
appflowy 2022-03-16 21:19:51 +08:00
parent 8c148e3b67
commit 1237962ab2
28 changed files with 763 additions and 81 deletions

View File

@ -4,7 +4,9 @@ import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/dart_notification.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
// User
typedef UserNotificationCallback = void Function(UserNotification, Either<Uint8List, FlowyError>);
class UserNotificationParser extends NotificationParser<UserNotification, FlowyError> {
@ -17,10 +19,11 @@ class UserNotificationParser extends NotificationParser<UserNotification, FlowyE
);
}
typedef NotificationCallback = void Function(FolderNotification, Either<Uint8List, FlowyError>);
// Folder
typedef FolderNotificationCallback = void Function(FolderNotification, Either<Uint8List, FlowyError>);
class FolderNotificationParser extends NotificationParser<FolderNotification, FlowyError> {
FolderNotificationParser({String? id, required NotificationCallback callback})
FolderNotificationParser({String? id, required FolderNotificationCallback callback})
: super(
id: id,
callback: callback,
@ -29,6 +32,19 @@ class FolderNotificationParser extends NotificationParser<FolderNotification, Fl
);
}
// Grid
typedef GridNotificationCallback = void Function(GridNotification, Either<Uint8List, FlowyError>);
class GridNotificationParser extends NotificationParser<GridNotification, FlowyError> {
GridNotificationParser({String? id, required GridNotificationCallback callback})
: super(
id: id,
callback: callback,
tyParser: (ty) => GridNotification.valueOf(ty),
errorParser: (bytes) => FlowyError.fromBuffer(bytes),
);
}
class NotificationParser<T, E> {
String? id;
void Function(T, Either<Uint8List, E>) callback;

View File

@ -16,6 +16,8 @@ import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_profile.pb.dart';
import 'package:get_it/get_it.dart';
import '../workspace/application/grid/grid_listener.dart';
class HomeDepsResolver {
static Future<void> resolve(GetIt getIt) async {
getIt.registerFactoryParam<UserListener, UserProfile, void>(
@ -90,10 +92,7 @@ class HomeDepsResolver {
// Grid
getIt.registerFactoryParam<GridBloc, View, void>(
(view, _) => GridBloc(
view: view,
service: GridService(),
),
(view, _) => GridBloc(view: view, service: GridService(), listener: GridListener(gridId: view.id)),
);
getIt.registerFactoryParam<RowBloc, GridRowData, void>(

View File

@ -1,13 +1,14 @@
import 'dart:async';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/log.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/protobuf.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'data.dart';
import 'grid_listener.dart';
import 'grid_service.dart';
part 'grid_bloc.freezed.dart';
@ -15,14 +16,16 @@ part 'grid_bloc.freezed.dart';
class GridBloc extends Bloc<GridEvent, GridState> {
final GridService service;
final View view;
final GridListener listener;
Grid? _grid;
List<Field>? _fields;
GridBloc({required this.view, required this.service}) : super(GridState.initial()) {
GridBloc({required this.view, required this.service, required this.listener}) : super(GridState.initial()) {
on<GridEvent>(
(event, emit) async {
await event.map(
initial: (InitialGrid value) async {
await _startGridListening();
await _loadGrid(emit);
await _loadFields(emit);
await _loadGridInfo(emit);
@ -40,9 +43,25 @@ class GridBloc extends Bloc<GridEvent, GridState> {
@override
Future<void> close() async {
await listener.close();
return super.close();
}
Future<void> _startGridListening() async {
listener.createRowNotifier.addPublishListener((result) {
result.fold((row) {
//
Log.info("$row");
}, (err) => null);
});
listener.deleteRowNotifier.addPublishListener((result) {
result.fold((l) => null, (r) => null);
});
listener.start();
}
Future<void> _loadGrid(Emitter<GridState> emit) async {
final result = await service.openGrid(gridId: view.id);
result.fold(

View File

@ -0,0 +1,60 @@
import 'package:flowy_sdk/protobuf/dart-notify/subject.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
import 'package:flowy_sdk/rust_stream.dart';
import 'package:flowy_infra/notifier.dart';
import 'dart:async';
import 'dart:typed_data';
import 'package:app_flowy/core/notification_helper.dart';
import 'package:dartz/dartz.dart';
typedef CreateRowNotifiedValue = Either<RepeatedRow, FlowyError>;
typedef DeleteRowNotifierValue = Either<RepeatedRow, FlowyError>;
class GridListener {
final String gridId;
PublishNotifier<CreateRowNotifiedValue> createRowNotifier = PublishNotifier<CreateRowNotifiedValue>();
PublishNotifier<DeleteRowNotifierValue> deleteRowNotifier = PublishNotifier<DeleteRowNotifierValue>();
StreamSubscription<SubscribeObject>? _subscription;
late GridNotificationParser _parser;
GridListener({required this.gridId});
void start() {
_parser = GridNotificationParser(
id: gridId,
callback: (ty, result) {
_handleObservableType(ty, result);
},
);
_subscription = RustStreamReceiver.listen((observable) => _parser.parse(observable));
}
void _handleObservableType(GridNotification ty, Either<Uint8List, FlowyError> result) {
switch (ty) {
case GridNotification.GridDidCreateRows:
result.fold(
(payload) => createRowNotifier.value = left(RepeatedRow.fromBuffer(payload)),
(error) => createRowNotifier.value = right(error),
);
break;
case GridNotification.GridDidDeleteRow:
result.fold(
(payload) => deleteRowNotifier.value = left(RepeatedRow.fromBuffer(payload)),
(error) => deleteRowNotifier.value = right(error),
);
break;
default:
break;
}
}
Future<void> close() async {
await _subscription?.cancel();
createRowNotifier.dispose();
deleteRowNotifier.dispose();
}
}

View File

@ -9,15 +9,15 @@ import 'package:flowy_sdk/protobuf/flowy-folder/dart_notification.pb.dart';
import 'package:flowy_sdk/rust_stream.dart';
import 'package:flowy_infra/notifier.dart';
typedef DeleteNotifierValue = Either<View, FlowyError>;
typedef UpdateNotifierValue = Either<View, FlowyError>;
typedef RestoreNotifierValue = Either<View, FlowyError>;
typedef DeleteViewNotifyValue = Either<View, FlowyError>;
typedef UpdateViewNotifiedValue = Either<View, FlowyError>;
typedef RestoreViewNotifiedValue = Either<View, FlowyError>;
class ViewListener {
StreamSubscription<SubscribeObject>? _subscription;
PublishNotifier<UpdateNotifierValue> updatedNotifier = PublishNotifier<UpdateNotifierValue>();
PublishNotifier<DeleteNotifierValue> deletedNotifier = PublishNotifier<DeleteNotifierValue>();
PublishNotifier<RestoreNotifierValue> restoredNotifier = PublishNotifier<RestoreNotifierValue>();
PublishNotifier<UpdateViewNotifiedValue> updatedNotifier = PublishNotifier<UpdateViewNotifiedValue>();
PublishNotifier<DeleteViewNotifyValue> deletedNotifier = PublishNotifier<DeleteViewNotifyValue>();
PublishNotifier<RestoreViewNotifiedValue> restoredNotifier = PublishNotifier<RestoreViewNotifiedValue>();
late FolderNotificationParser _parser;
View view;

View File

@ -139,9 +139,10 @@ class _GridBodyState extends State<GridBody> {
delegate: SliverChildBuilderDelegate(
(context, index) {
final data = gridInfo.rowAtIndex(index);
return RepaintBoundary(child: GridRowWidget(data: data));
return GridRowWidget(data: data);
},
childCount: gridInfo.numberOfRows(),
addRepaintBoundaries: true,
),
);
}

View File

@ -607,6 +607,47 @@ class Cell extends $pb.GeneratedMessage {
void clearContent() => clearField(2);
}
class RepeatedCell extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RepeatedCell', createEmptyInstance: create)
..pc<Cell>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'items', $pb.PbFieldType.PM, subBuilder: Cell.create)
..hasRequiredFields = false
;
RepeatedCell._() : super();
factory RepeatedCell({
$core.Iterable<Cell>? items,
}) {
final _result = create();
if (items != null) {
_result.items.addAll(items);
}
return _result;
}
factory RepeatedCell.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory RepeatedCell.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
RepeatedCell clone() => RepeatedCell()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
RepeatedCell copyWith(void Function(RepeatedCell) updates) => super.copyWith((message) => updates(message as RepeatedCell)) as RepeatedCell; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static RepeatedCell create() => RepeatedCell._();
RepeatedCell createEmptyInstance() => create();
static $pb.PbList<RepeatedCell> createRepeated() => $pb.PbList<RepeatedCell>();
@$core.pragma('dart2js:noInline')
static RepeatedCell getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RepeatedCell>(create);
static RepeatedCell? _defaultInstance;
@$pb.TagNumber(1)
$core.List<Cell> get items => $_getList(0);
}
class CreateGridPayload extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CreateGridPayload', createEmptyInstance: create)
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')

View File

@ -131,6 +131,16 @@ const Cell$json = const {
/// Descriptor for `Cell`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List cellDescriptor = $convert.base64Decode('CgRDZWxsEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElkEhgKB2NvbnRlbnQYAiABKAlSB2NvbnRlbnQ=');
@$core.Deprecated('Use repeatedCellDescriptor instead')
const RepeatedCell$json = const {
'1': 'RepeatedCell',
'2': const [
const {'1': 'items', '3': 1, '4': 3, '5': 11, '6': '.Cell', '10': 'items'},
],
};
/// Descriptor for `RepeatedCell`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List repeatedCellDescriptor = $convert.base64Decode('CgxSZXBlYXRlZENlbGwSGwoFaXRlbXMYASADKAsyBS5DZWxsUgVpdGVtcw==');
@$core.Deprecated('Use createGridPayloadDescriptor instead')
const CreateGridPayload$json = const {
'1': 'CreateGridPayload',

View File

@ -0,0 +1,11 @@
///
// Generated code. Do not modify.
// source: dart_notification.proto
//
// @dart = 2.12
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
import 'dart:core' as $core;
export 'dart_notification.pbenum.dart';

View File

@ -0,0 +1,34 @@
///
// Generated code. Do not modify.
// source: dart_notification.proto
//
// @dart = 2.12
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
// ignore_for_file: UNDEFINED_SHOWN_NAME
import 'dart:core' as $core;
import 'package:protobuf/protobuf.dart' as $pb;
class GridNotification extends $pb.ProtobufEnum {
static const GridNotification Unknown = GridNotification._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Unknown');
static const GridNotification GridDidCreateRows = GridNotification._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GridDidCreateRows');
static const GridNotification GridDidDeleteRow = GridNotification._(11, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GridDidDeleteRow');
static const GridNotification GridDidUpdateRows = GridNotification._(12, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GridDidUpdateRows');
static const GridNotification GridDidUpdateCells = GridNotification._(20, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GridDidUpdateCells');
static const GridNotification GridDidUpdateFields = GridNotification._(30, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GridDidUpdateFields');
static const $core.List<GridNotification> values = <GridNotification> [
Unknown,
GridDidCreateRows,
GridDidDeleteRow,
GridDidUpdateRows,
GridDidUpdateCells,
GridDidUpdateFields,
];
static final $core.Map<$core.int, GridNotification> _byValue = $pb.ProtobufEnum.initByValue(values);
static GridNotification? valueOf($core.int value) => _byValue[value];
const GridNotification._($core.int v, $core.String n) : super(v, n);
}

View File

@ -0,0 +1,25 @@
///
// Generated code. Do not modify.
// source: dart_notification.proto
//
// @dart = 2.12
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
import 'dart:core' as $core;
import 'dart:convert' as $convert;
import 'dart:typed_data' as $typed_data;
@$core.Deprecated('Use gridNotificationDescriptor instead')
const GridNotification$json = const {
'1': 'GridNotification',
'2': const [
const {'1': 'Unknown', '2': 0},
const {'1': 'GridDidCreateRows', '2': 10},
const {'1': 'GridDidDeleteRow', '2': 11},
const {'1': 'GridDidUpdateRows', '2': 12},
const {'1': 'GridDidUpdateCells', '2': 20},
const {'1': 'GridDidUpdateFields', '2': 30},
],
};
/// Descriptor for `GridNotification`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List gridNotificationDescriptor = $convert.base64Decode('ChBHcmlkTm90aWZpY2F0aW9uEgsKB1Vua25vd24QABIVChFHcmlkRGlkQ3JlYXRlUm93cxAKEhQKEEdyaWREaWREZWxldGVSb3cQCxIVChFHcmlkRGlkVXBkYXRlUm93cxAMEhYKEkdyaWREaWRVcGRhdGVDZWxscxAUEhcKE0dyaWREaWRVcGRhdGVGaWVsZHMQHg==');

View File

@ -0,0 +1,9 @@
///
// Generated code. Do not modify.
// source: dart_notification.proto
//
// @dart = 2.12
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
export 'dart_notification.pb.dart';

View File

@ -1,6 +1,7 @@
// Auto-generated, do not edit
export './date_description.pb.dart';
export './text_description.pb.dart';
export './dart_notification.pb.dart';
export './checkbox_description.pb.dart';
export './selection_description.pb.dart';
export './event_map.pb.dart';

View File

@ -1,3 +1,3 @@
proto_crates = ["src/event_map.rs", "src/services/cell/description"]
proto_crates = ["src/event_map.rs", "src/services/cell/description", "src/dart_notification.rs"]
event_files = ["src/event_map.rs"]

View File

@ -0,0 +1,36 @@
use dart_notify::DartNotifyBuilder;
use flowy_derive::ProtoBuf_Enum;
const OBSERVABLE_CATEGORY: &str = "Grid";
#[derive(ProtoBuf_Enum, Debug)]
pub enum GridNotification {
Unknown = 0,
GridDidCreateRows = 10,
GridDidDeleteRow = 11,
GridDidUpdateRows = 12,
GridDidUpdateCells = 20,
GridDidUpdateFields = 30,
}
impl std::default::Default for GridNotification {
fn default() -> Self {
GridNotification::Unknown
}
}
impl std::convert::From<GridNotification> for i32 {
fn from(notification: GridNotification) -> Self {
notification as i32
}
}
#[tracing::instrument(level = "trace")]
pub fn send_dart_notification(id: &str, ty: GridNotification) -> DartNotifyBuilder {
DartNotifyBuilder::new(id, ty, OBSERVABLE_CATEGORY)
}
#[tracing::instrument(level = "trace")]
pub fn send_anonymous_dart_notification(ty: GridNotification) -> DartNotifyBuilder {
DartNotifyBuilder::new("", ty, OBSERVABLE_CATEGORY)
}

View File

@ -45,11 +45,11 @@ pub(crate) async fn get_fields_handler(
pub(crate) async fn create_row_handler(
data: Data<CreateRowPayload>,
manager: AppData<Arc<GridManager>>,
) -> DataResult<Row, FlowyError> {
) -> Result<(), FlowyError> {
let payload: CreateRowPayload = data.into_inner();
let editor = manager.get_grid_editor(payload.grid_id.as_ref())?;
let row = editor.create_row(payload.upper_row_id).await?;
data_result(row)
let _ = editor.create_row(payload.upper_row_id).await?;
Ok(())
}
#[tracing::instrument(level = "debug", skip_all, err)]

View File

@ -5,6 +5,7 @@ mod event_handler;
pub mod event_map;
pub mod manager;
mod dart_notification;
mod protobuf;
pub mod services;
pub mod util;

View File

@ -0,0 +1,106 @@
// This file is generated by rust-protobuf 2.25.2. Do not edit
// @generated
// https://github.com/rust-lang/rust-clippy/issues/702
#![allow(unknown_lints)]
#![allow(clippy::all)]
#![allow(unused_attributes)]
#![cfg_attr(rustfmt, rustfmt::skip)]
#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(trivial_casts)]
#![allow(unused_imports)]
#![allow(unused_results)]
//! Generated file from `dart_notification.proto`
/// Generated files are compatible only with the same version
/// of protobuf runtime.
// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_25_2;
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum GridNotification {
Unknown = 0,
GridDidCreateRows = 10,
GridDidDeleteRow = 11,
GridDidUpdateRows = 12,
GridDidUpdateCells = 20,
GridDidUpdateFields = 30,
}
impl ::protobuf::ProtobufEnum for GridNotification {
fn value(&self) -> i32 {
*self as i32
}
fn from_i32(value: i32) -> ::std::option::Option<GridNotification> {
match value {
0 => ::std::option::Option::Some(GridNotification::Unknown),
10 => ::std::option::Option::Some(GridNotification::GridDidCreateRows),
11 => ::std::option::Option::Some(GridNotification::GridDidDeleteRow),
12 => ::std::option::Option::Some(GridNotification::GridDidUpdateRows),
20 => ::std::option::Option::Some(GridNotification::GridDidUpdateCells),
30 => ::std::option::Option::Some(GridNotification::GridDidUpdateFields),
_ => ::std::option::Option::None
}
}
fn values() -> &'static [Self] {
static values: &'static [GridNotification] = &[
GridNotification::Unknown,
GridNotification::GridDidCreateRows,
GridNotification::GridDidDeleteRow,
GridNotification::GridDidUpdateRows,
GridNotification::GridDidUpdateCells,
GridNotification::GridDidUpdateFields,
];
values
}
fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
::protobuf::reflect::EnumDescriptor::new_pb_name::<GridNotification>("GridNotification", file_descriptor_proto())
})
}
}
impl ::std::marker::Copy for GridNotification {
}
impl ::std::default::Default for GridNotification {
fn default() -> Self {
GridNotification::Unknown
}
}
impl ::protobuf::reflect::ProtobufValue for GridNotification {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
}
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x17dart_notification.proto*\x94\x01\n\x10GridNotification\x12\x0b\n\
\x07Unknown\x10\0\x12\x15\n\x11GridDidCreateRows\x10\n\x12\x14\n\x10Grid\
DidDeleteRow\x10\x0b\x12\x15\n\x11GridDidUpdateRows\x10\x0c\x12\x16\n\
\x12GridDidUpdateCells\x10\x14\x12\x17\n\x13GridDidUpdateFields\x10\x1eb\
\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
}
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
file_descriptor_proto_lazy.get(|| {
parse_descriptor_proto()
})
}

View File

@ -7,6 +7,9 @@ pub use date_description::*;
mod text_description;
pub use text_description::*;
mod dart_notification;
pub use dart_notification::*;
mod checkbox_description;
pub use checkbox_description::*;

View File

@ -0,0 +1,10 @@
syntax = "proto3";
enum GridNotification {
Unknown = 0;
GridDidCreateRows = 10;
GridDidDeleteRow = 11;
GridDidUpdateRows = 12;
GridDidUpdateCells = 20;
GridDidUpdateFields = 30;
}

View File

@ -1,5 +1,5 @@
use crate::manager::GridUser;
use crate::services::row::make_row_ids_per_block;
use crate::services::row::{make_cell, make_row_ids_per_block, make_rows};
use bytes::Bytes;
use dashmap::DashMap;
@ -8,7 +8,8 @@ use flowy_collaboration::entities::revision::Revision;
use flowy_collaboration::util::make_delta_from_revisions;
use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::entities::{
GridBlock, GridBlockChangeset, RepeatedRowOrder, RowMeta, RowMetaChangeset, RowOrder,
Cell, FieldMeta, GridBlock, GridBlockChangeset, RepeatedCell, RepeatedRow, RepeatedRowOrder, RowMeta,
RowMetaChangeset, RowOrder,
};
use flowy_sync::disk::SQLiteGridBlockMetaRevisionPersistence;
use flowy_sync::{
@ -19,6 +20,7 @@ use lib_ot::core::PlainTextAttributes;
use std::collections::HashMap;
use crate::dart_notification::{send_dart_notification, GridNotification};
use std::sync::Arc;
use tokio::sync::RwLock;
@ -26,17 +28,20 @@ type RowId = String;
type BlockId = String;
pub(crate) struct GridBlockMetaEditorManager {
grid_id: String,
user: Arc<dyn GridUser>,
editor_map: DashMap<String, Arc<ClientGridBlockMetaEditor>>,
block_id_by_row_id: DashMap<BlockId, RowId>,
}
impl GridBlockMetaEditorManager {
pub(crate) async fn new(user: &Arc<dyn GridUser>, blocks: Vec<GridBlock>) -> FlowyResult<Self> {
pub(crate) async fn new(grid_id: &str, user: &Arc<dyn GridUser>, blocks: Vec<GridBlock>) -> FlowyResult<Self> {
let editor_map = make_block_meta_editor_map(user, blocks).await?;
let user = user.clone();
let block_id_by_row_id = DashMap::new();
let grid_id = grid_id.to_owned();
let manager = Self {
grid_id,
user,
editor_map,
block_id_by_row_id,
@ -56,25 +61,41 @@ impl GridBlockMetaEditorManager {
}
}
pub(crate) async fn create_row(&self, row: RowMeta, upper_row_id: Option<String>) -> FlowyResult<i32> {
self.block_id_by_row_id.insert(row.id.clone(), row.block_id.clone());
let editor = self.get_editor(&row.block_id).await?;
editor.create_row(row, upper_row_id).await
pub(crate) async fn create_row(
&self,
field_metas: &[FieldMeta],
row_meta: RowMeta,
upper_row_id: Option<String>,
) -> FlowyResult<i32> {
self.block_id_by_row_id
.insert(row_meta.id.clone(), row_meta.block_id.clone());
let editor = self.get_editor(&row_meta.block_id).await?;
let rows = make_rows(field_metas, vec![row_meta.clone().into()]);
send_dart_notification(&self.grid_id, GridNotification::GridDidCreateRows)
.payload(RepeatedRow::from(rows))
.send();
self.notify_did_create_rows(field_metas, vec![row_meta.clone()]);
editor.create_row(row_meta, upper_row_id).await
}
pub(crate) async fn insert_row(
&self,
field_metas: &[FieldMeta],
rows_by_block_id: HashMap<String, Vec<RowMeta>>,
) -> FlowyResult<Vec<GridBlockChangeset>> {
let mut changesets = vec![];
for (block_id, rows) in rows_by_block_id {
for (block_id, row_metas) in rows_by_block_id {
let editor = self.get_editor(&block_id).await?;
let mut row_count = 0;
for row in rows {
for row in &row_metas {
self.block_id_by_row_id.insert(row.id.clone(), row.block_id.clone());
row_count = editor.create_row(row, None).await?;
row_count = editor.create_row(row.clone(), None).await?;
}
changesets.push(GridBlockChangeset::from_row_count(&block_id, row_count));
self.notify_did_create_rows(field_metas, row_metas);
}
Ok(changesets)
@ -104,19 +125,17 @@ impl GridBlockMetaEditorManager {
}
pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> {
match self.block_id_by_row_id.get(&changeset.row_id) {
None => {
let msg = format!(
"Update Row failed. Can't find the corresponding block with row_id: {}",
changeset.row_id
);
Err(FlowyError::internal().context(msg))
}
Some(block_id) => {
let editor = self.get_editor(&block_id).await?;
editor.update_row(changeset).await
}
}
let editor = self.get_editor_from_row_id(&changeset.row_id).await?;
let _ = editor.update_row(changeset.clone()).await?;
let _ = self.notify_did_update_row()?;
Ok(())
}
pub async fn update_cells(&self, field_metas: &[FieldMeta], changeset: RowMetaChangeset) -> FlowyResult<()> {
let editor = self.get_editor_from_row_id(&changeset.row_id).await?;
let _ = editor.update_row(changeset.clone()).await?;
self.notify_did_update_cells(changeset, field_metas)?;
Ok(())
}
pub(crate) async fn get_all_rows(&self, grid_blocks: Vec<GridBlock>) -> FlowyResult<Vec<Arc<RowMeta>>> {
@ -159,6 +178,68 @@ impl GridBlockMetaEditorManager {
}
Ok(row_metas)
}
async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaEditor>> {
match self.block_id_by_row_id.get(row_id) {
None => {
let msg = format!(
"Update Row failed. Can't find the corresponding block with row_id: {}",
row_id
);
Err(FlowyError::internal().context(msg))
}
Some(block_id) => {
let editor = self.get_editor(&block_id).await?;
Ok(editor)
}
}
}
fn notify_did_create_rows(&self, field_metas: &[FieldMeta], row_metas: Vec<RowMeta>) {
let rows = make_rows(
field_metas,
row_metas
.into_iter()
.map(|row_meta| Arc::new(row_meta))
.collect::<Vec<_>>(),
);
send_dart_notification(&self.grid_id, GridNotification::GridDidCreateRows)
.payload(RepeatedRow::from(rows))
.send();
}
fn notify_did_update_row(&self) -> FlowyResult<()> {
// send_dart_notification(&changeset.row_id, GridNotification::GridDidUpdateRows)
// .payload(RepeatedRow::from(cells))
// .send();
todo!()
}
fn notify_did_update_cells(&self, changeset: RowMetaChangeset, field_metas: &[FieldMeta]) -> FlowyResult<()> {
let field_meta_map = field_metas
.iter()
.map(|field_meta| (&field_meta.id, field_meta))
.collect::<HashMap<&String, &FieldMeta>>();
let mut cells = vec![];
changeset
.cell_by_field_id
.into_iter()
.for_each(
|(field_id, cell_meta)| match make_cell(&field_meta_map, field_id, cell_meta) {
None => {}
Some((_, cell)) => cells.push(cell),
},
);
if !cells.is_empty() {
send_dart_notification(&changeset.row_id, GridNotification::GridDidUpdateCells)
.payload(RepeatedCell::from(cells))
.send();
}
Ok(())
}
}
async fn make_block_meta_editor_map(

View File

@ -1,16 +1,17 @@
use crate::manager::GridUser;
use crate::services::block_meta_editor::GridBlockMetaEditorManager;
use bytes::Bytes;
use flowy_collaboration::client_grid::{GridChange, GridMetaPad};
use flowy_collaboration::client_grid::{GridChangeset, GridMetaPad};
use flowy_collaboration::entities::revision::Revision;
use flowy_collaboration::util::make_delta_from_revisions;
use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::entities::{
CellMetaChangeset, FieldChangeset, FieldMeta, Grid, GridBlock, GridBlockChangeset, RepeatedFieldOrder,
RepeatedRowOrder, Row, RowMeta, RowMetaChangeset,
Cell, CellMetaChangeset, Field, FieldChangeset, FieldMeta, Grid, GridBlock, GridBlockChangeset, RepeatedField,
RepeatedFieldOrder, RepeatedRow, RepeatedRowOrder, Row, RowMeta, RowMetaChangeset,
};
use std::collections::HashMap;
use crate::dart_notification::{send_dart_notification, GridNotification};
use crate::services::row::{
make_row_by_row_id, make_rows, row_meta_from_context, serialize_cell_data, RowMetaContext, RowMetaContextBuilder,
};
@ -40,8 +41,9 @@ impl ClientGridEditor {
let rev_manager = Arc::new(rev_manager);
let grid_meta_pad = Arc::new(RwLock::new(grid_pad));
let block_meta_manager =
Arc::new(GridBlockMetaEditorManager::new(&user, grid_meta_pad.read().await.get_blocks().clone()).await?);
let block_meta_manager = Arc::new(
GridBlockMetaEditorManager::new(grid_id, &user, grid_meta_pad.read().await.get_blocks().clone()).await?,
);
Ok(Arc::new(Self {
grid_id: grid_id.to_owned(),
@ -54,6 +56,7 @@ impl ClientGridEditor {
pub async fn create_field(&self, field_meta: FieldMeta) -> FlowyResult<()> {
let _ = self.modify(|grid| Ok(grid.create_field(field_meta)?)).await?;
let _ = self.notify_did_update_fields().await?;
Ok(())
}
@ -81,7 +84,7 @@ impl ClientGridEditor {
Ok(())
}
pub async fn create_row(&self, upper_row_id: Option<String>) -> FlowyResult<Row> {
pub async fn create_row(&self, upper_row_id: Option<String>) -> FlowyResult<()> {
let field_metas = self.grid_meta_pad.read().await.get_field_metas(None)?;
let block_id = self.last_block_id().await?;
@ -92,17 +95,17 @@ impl ClientGridEditor {
// insert the row
let row_count = self
.block_meta_manager
.create_row(row_meta.clone(), upper_row_id)
.create_row(&field_metas, row_meta, upper_row_id)
.await?;
let row = make_rows(&field_metas, vec![row_meta.into()]).pop().unwrap();
// update block row count
let changeset = GridBlockChangeset::from_row_count(&block_id, row_count);
let _ = self.update_block(changeset).await?;
Ok(row)
Ok(())
}
pub async fn insert_rows(&self, contexts: Vec<RowMetaContext>) -> FlowyResult<()> {
let field_metas = self.grid_meta_pad.read().await.get_field_metas(None)?;
let block_id = self.last_block_id().await?;
let mut rows_by_block_id: HashMap<String, Vec<RowMeta>> = HashMap::new();
for ctx in contexts {
@ -112,7 +115,10 @@ impl ClientGridEditor {
.or_insert_with(Vec::new)
.push(row_meta);
}
let changesets = self.block_meta_manager.insert_row(rows_by_block_id).await?;
let changesets = self
.block_meta_manager
.insert_row(&field_metas, rows_by_block_id)
.await?;
for changeset in changesets {
let _ = self.update_block(changeset).await?;
}
@ -136,8 +142,13 @@ impl ClientGridEditor {
}
}
let field_metas = self.get_field_metas(None).await?;
let row_changeset: RowMetaChangeset = changeset.into();
self.update_row(row_changeset).await
let _ = self
.block_meta_manager
.update_cells(&field_metas, row_changeset)
.await?;
Ok(())
}
pub async fn get_rows(&self, row_orders: Option<RepeatedRowOrder>) -> FlowyResult<Vec<Row>> {
@ -205,7 +216,7 @@ impl ClientGridEditor {
async fn modify<F>(&self, f: F) -> FlowyResult<()>
where
F: for<'a> FnOnce(&'a mut GridMetaPad) -> FlowyResult<Option<GridChange>>,
F: for<'a> FnOnce(&'a mut GridMetaPad) -> FlowyResult<Option<GridChangeset>>,
{
let mut write_guard = self.grid_meta_pad.write().await;
match f(&mut *write_guard)? {
@ -217,8 +228,8 @@ impl ClientGridEditor {
Ok(())
}
async fn apply_change(&self, change: GridChange) -> FlowyResult<()> {
let GridChange { delta, md5 } = change;
async fn apply_change(&self, change: GridChangeset) -> FlowyResult<()> {
let GridChangeset { delta, md5 } = change;
let user_id = self.user.user_id()?;
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
let delta_data = delta.to_delta_bytes();
@ -243,6 +254,15 @@ impl ClientGridEditor {
Some(grid_block) => Ok(grid_block.id.clone()),
}
}
async fn notify_did_update_fields(&self) -> FlowyResult<()> {
let field_metas = self.get_field_metas(None).await?;
let repeated_field: RepeatedField = field_metas.into_iter().map(Field::from).collect::<Vec<_>>().into();
send_dart_notification(&self.grid_id, GridNotification::GridDidUpdateFields)
.payload(repeated_field)
.send();
Ok(())
}
}
#[cfg(feature = "flowy_unit_test")]

View File

@ -48,7 +48,11 @@ pub(crate) fn make_rows(fields: &[FieldMeta], row_metas: Vec<Arc<RowMeta>>) -> V
}
#[inline(always)]
fn make_cell(field_map: &HashMap<&String, &FieldMeta>, field_id: String, raw_cell: CellMeta) -> Option<(String, Cell)> {
pub fn make_cell(
field_map: &HashMap<&String, &FieldMeta>,
field_id: String,
raw_cell: CellMeta,
) -> Option<(String, Cell)> {
let field_meta = field_map.get(&field_id)?;
match deserialize_cell_data(raw_cell.data, field_meta) {
Ok(content) => {
@ -63,9 +67,9 @@ fn make_cell(field_map: &HashMap<&String, &FieldMeta>, field_id: String, raw_cel
}
pub(crate) fn make_row_by_row_id(fields: &[FieldMeta], row_metas: Vec<Arc<RowMeta>>) -> HashMap<String, Row> {
let field_map = fields
let field_meta_map = fields
.iter()
.map(|field| (&field.id, field))
.map(|field_meta| (&field_meta.id, field_meta))
.collect::<HashMap<&String, &FieldMeta>>();
let make_row = |row_meta: Arc<RowMeta>| {
@ -73,7 +77,7 @@ pub(crate) fn make_row_by_row_id(fields: &[FieldMeta], row_metas: Vec<Arc<RowMet
.cell_by_field_id
.clone()
.into_par_iter()
.flat_map(|(field_id, raw_cell)| make_cell(&field_map, field_id, raw_cell))
.flat_map(|(field_id, raw_cell)| make_cell(&field_meta_map, field_id, raw_cell))
.collect::<HashMap<String, Cell>>();
let row = Row {

View File

@ -172,7 +172,7 @@ impl GridEditorTest {
assert_eq!(compared_block, block);
}
EditorScript::CreateEmptyRow => {
self.editor.create_row().await.unwrap();
self.editor.create_row(None).await.unwrap();
self.row_metas = self.editor.get_row_metas(None).await.unwrap();
self.grid_blocks = self.editor.get_blocks().await.unwrap();
}

View File

@ -35,7 +35,7 @@ impl GridMetaPad {
Self::from_delta(grid_delta)
}
pub fn create_field(&mut self, field_meta: FieldMeta) -> CollaborateResult<Option<GridChange>> {
pub fn create_field(&mut self, field_meta: FieldMeta) -> CollaborateResult<Option<GridChangeset>> {
self.modify_grid(|grid| {
if grid.fields.contains(&field_meta) {
tracing::warn!("Duplicate grid field");
@ -47,7 +47,7 @@ impl GridMetaPad {
})
}
pub fn delete_field(&mut self, field_id: &str) -> CollaborateResult<Option<GridChange>> {
pub fn delete_field(&mut self, field_id: &str) -> CollaborateResult<Option<GridChangeset>> {
self.modify_grid(|grid| match grid.fields.iter().position(|field| field.id == field_id) {
None => Ok(None),
Some(index) => {
@ -95,7 +95,7 @@ impl GridMetaPad {
}
}
pub fn update_field(&mut self, changeset: FieldChangeset) -> CollaborateResult<Option<GridChange>> {
pub fn update_field(&mut self, changeset: FieldChangeset) -> CollaborateResult<Option<GridChangeset>> {
let field_id = changeset.field_id.clone();
self.modify_field(&field_id, |field| {
let mut is_changed = None;
@ -138,7 +138,7 @@ impl GridMetaPad {
})
}
pub fn create_block(&mut self, block: GridBlock) -> CollaborateResult<Option<GridChange>> {
pub fn create_block(&mut self, block: GridBlock) -> CollaborateResult<Option<GridChangeset>> {
self.modify_grid(|grid| {
if grid.blocks.iter().any(|b| b.id == block.id) {
tracing::warn!("Duplicate grid block");
@ -165,7 +165,7 @@ impl GridMetaPad {
self.grid_meta.blocks.clone()
}
pub fn update_block(&mut self, changeset: GridBlockChangeset) -> CollaborateResult<Option<GridChange>> {
pub fn update_block(&mut self, changeset: GridBlockChangeset) -> CollaborateResult<Option<GridChangeset>> {
let block_id = changeset.block_id.clone();
self.modify_block(&block_id, |block| {
let mut is_changed = None;
@ -200,7 +200,7 @@ impl GridMetaPad {
&self.grid_meta.fields
}
fn modify_grid<F>(&mut self, f: F) -> CollaborateResult<Option<GridChange>>
fn modify_grid<F>(&mut self, f: F) -> CollaborateResult<Option<GridChangeset>>
where
F: FnOnce(&mut GridMeta) -> CollaborateResult<Option<()>>,
{
@ -214,14 +214,14 @@ impl GridMetaPad {
None => Ok(None),
Some(delta) => {
self.delta = self.delta.compose(&delta)?;
Ok(Some(GridChange { delta, md5: self.md5() }))
Ok(Some(GridChangeset { delta, md5: self.md5() }))
}
}
}
}
}
pub fn modify_block<F>(&mut self, block_id: &str, f: F) -> CollaborateResult<Option<GridChange>>
pub fn modify_block<F>(&mut self, block_id: &str, f: F) -> CollaborateResult<Option<GridChangeset>>
where
F: FnOnce(&mut GridBlock) -> CollaborateResult<Option<()>>,
{
@ -234,7 +234,7 @@ impl GridMetaPad {
})
}
pub fn modify_field<F>(&mut self, field_id: &str, f: F) -> CollaborateResult<Option<GridChange>>
pub fn modify_field<F>(&mut self, field_id: &str, f: F) -> CollaborateResult<Option<GridChangeset>>
where
F: FnOnce(&mut FieldMeta) -> CollaborateResult<Option<()>>,
{
@ -254,7 +254,7 @@ fn json_from_grid(grid: &Arc<GridMeta>) -> CollaborateResult<String> {
Ok(json)
}
pub struct GridChange {
pub struct GridChangeset {
pub delta: GridMetaDelta,
/// md5: the md5 of the grid after applying the change.
pub md5: String,

View File

@ -204,6 +204,31 @@ impl Cell {
}
}
#[derive(Debug, Default, ProtoBuf)]
pub struct RepeatedCell {
#[pb(index = 1)]
pub items: Vec<Cell>,
}
impl std::ops::Deref for RepeatedCell {
type Target = Vec<Cell>;
fn deref(&self) -> &Self::Target {
&self.items
}
}
impl std::ops::DerefMut for RepeatedCell {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.items
}
}
impl std::convert::From<Vec<Cell>> for RepeatedCell {
fn from(items: Vec<Cell>) -> Self {
Self { items }
}
}
#[derive(ProtoBuf, Default)]
pub struct CreateGridPayload {
#[pb(index = 1)]

View File

@ -2115,6 +2115,172 @@ impl ::protobuf::reflect::ProtobufValue for Cell {
}
}
#[derive(PartialEq,Clone,Default)]
pub struct RepeatedCell {
// message fields
pub items: ::protobuf::RepeatedField<Cell>,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a RepeatedCell {
fn default() -> &'a RepeatedCell {
<RepeatedCell as ::protobuf::Message>::default_instance()
}
}
impl RepeatedCell {
pub fn new() -> RepeatedCell {
::std::default::Default::default()
}
// repeated .Cell items = 1;
pub fn get_items(&self) -> &[Cell] {
&self.items
}
pub fn clear_items(&mut self) {
self.items.clear();
}
// Param is passed by value, moved
pub fn set_items(&mut self, v: ::protobuf::RepeatedField<Cell>) {
self.items = v;
}
// Mutable pointer to the field.
pub fn mut_items(&mut self) -> &mut ::protobuf::RepeatedField<Cell> {
&mut self.items
}
// Take field
pub fn take_items(&mut self) -> ::protobuf::RepeatedField<Cell> {
::std::mem::replace(&mut self.items, ::protobuf::RepeatedField::new())
}
}
impl ::protobuf::Message for RepeatedCell {
fn is_initialized(&self) -> bool {
for v in &self.items {
if !v.is_initialized() {
return false;
}
};
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.items)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
for value in &self.items {
let len = value.compute_size();
my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
};
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
for v in &self.items {
os.write_tag(1, ::protobuf::wire_format::WireTypeLengthDelimited)?;
os.write_raw_varint32(v.get_cached_size())?;
v.write_to_with_cached_sizes(os)?;
};
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &dyn (::std::any::Any) {
self as &dyn (::std::any::Any)
}
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
self as &mut dyn (::std::any::Any)
}
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> RepeatedCell {
RepeatedCell::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<Cell>>(
"items",
|m: &RepeatedCell| { &m.items },
|m: &mut RepeatedCell| { &mut m.items },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<RepeatedCell>(
"RepeatedCell",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static RepeatedCell {
static instance: ::protobuf::rt::LazyV2<RepeatedCell> = ::protobuf::rt::LazyV2::INIT;
instance.get(RepeatedCell::new)
}
}
impl ::protobuf::Clear for RepeatedCell {
fn clear(&mut self) {
self.items.clear();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for RepeatedCell {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for RepeatedCell {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(PartialEq,Clone,Default)]
pub struct CreateGridPayload {
// message fields
@ -3129,16 +3295,17 @@ static file_descriptor_proto_data: &'static [u8] = b"\
\x02\x20\x01(\x0b2\x05.CellR\x05value:\x028\x01\")\n\x0bRepeatedRow\x12\
\x1a\n\x05items\x18\x01\x20\x03(\x0b2\x04.RowR\x05items\";\n\x04Cell\x12\
\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\x12\x18\n\x07content\
\x18\x02\x20\x01(\tR\x07content\"'\n\x11CreateGridPayload\x12\x12\n\x04n\
ame\x18\x01\x20\x01(\tR\x04name\"\x1e\n\x06GridId\x12\x14\n\x05value\x18\
\x01\x20\x01(\tR\x05value\"f\n\x10CreateRowPayload\x12\x17\n\x07grid_id\
\x18\x01\x20\x01(\tR\x06gridId\x12\"\n\x0cupper_row_id\x18\x02\x20\x01(\
\tH\0R\nupperRowIdB\x15\n\x13one_of_upper_row_id\"d\n\x11QueryFieldPaylo\
ad\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x126\n\x0cfield_or\
ders\x18\x02\x20\x01(\x0b2\x13.RepeatedFieldOrderR\x0bfieldOrders\"\\\n\
\x0fQueryRowPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\
\x120\n\nrow_orders\x18\x02\x20\x01(\x0b2\x11.RepeatedRowOrderR\trowOrde\
rsb\x06proto3\
\x18\x02\x20\x01(\tR\x07content\"+\n\x0cRepeatedCell\x12\x1b\n\x05items\
\x18\x01\x20\x03(\x0b2\x05.CellR\x05items\"'\n\x11CreateGridPayload\x12\
\x12\n\x04name\x18\x01\x20\x01(\tR\x04name\"\x1e\n\x06GridId\x12\x14\n\
\x05value\x18\x01\x20\x01(\tR\x05value\"f\n\x10CreateRowPayload\x12\x17\
\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\"\n\x0cupper_row_id\x18\
\x02\x20\x01(\tH\0R\nupperRowIdB\x15\n\x13one_of_upper_row_id\"d\n\x11Qu\
eryFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x126\
\n\x0cfield_orders\x18\x02\x20\x01(\x0b2\x13.RepeatedFieldOrderR\x0bfiel\
dOrders\"\\\n\x0fQueryRowPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\t\
R\x06gridId\x120\n\nrow_orders\x18\x02\x20\x01(\x0b2\x11.RepeatedRowOrde\
rR\trowOrdersb\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -43,6 +43,9 @@ message Cell {
string field_id = 1;
string content = 2;
}
message RepeatedCell {
repeated Cell items = 1;
}
message CreateGridPayload {
string name = 1;
}