chore: add sub data type

This commit is contained in:
appflowy 2022-08-10 17:59:28 +08:00
parent 2b745bc41a
commit ad3e2f5725
29 changed files with 243 additions and 166 deletions

View File

@ -6,6 +6,7 @@ import 'package:app_flowy/plugins/grid/presentation/widgets/header/type_option/b
import 'package:appflowy_board/appflowy_board.dart';
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
@ -55,15 +56,6 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
didReceiveGridUpdate: (GridPB grid) {
emit(state.copyWith(grid: Some(grid)));
},
didReceiveFieldUpdate: (UnmodifiableListView<GridFieldPB> fields) {
emit(state.copyWith(fields: GridFieldEquatable(fields)));
},
didReceiveRowUpdate: (
List<GridRowInfo> newRowInfos,
GridRowChangeReason reason,
) {
emit(state.copyWith(rowInfos: newRowInfos, reason: reason));
},
);
},
);
@ -89,18 +81,21 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
},
onRowsChanged: (rowInfos, reason) {
if (!isClosed) {
add(BoardEvent.didReceiveRowUpdate(rowInfos, reason));
_buildColumnItems(rowInfos);
}
},
onFieldsChanged: (fields) {
if (!isClosed) {
_buildColumns(fields);
add(BoardEvent.didReceiveFieldUpdate(fields));
}
},
);
}
void _buildColumnItems(List<GridRowInfo> rowInfos) {
for (final rowInfo in rowInfos) {}
}
void _buildColumns(UnmodifiableListView<GridFieldPB> fields) {
for (final field in fields) {
if (field.fieldType == FieldType.SingleSelect) {
@ -127,7 +122,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
boardDataController.addColumns(columns);
},
onError: (err) {},
onError: (err) => Log.error(err),
);
}
@ -148,14 +143,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
class BoardEvent with _$BoardEvent {
const factory BoardEvent.initial() = InitialGrid;
const factory BoardEvent.createRow() = _CreateRow;
const factory BoardEvent.didReceiveRowUpdate(
List<GridRowInfo> rows,
GridRowChangeReason listState,
) = _DidReceiveRowUpdate;
const factory BoardEvent.didReceiveFieldUpdate(
UnmodifiableListView<GridFieldPB> fields,
) = _DidReceiveFieldUpdate;
const factory BoardEvent.groupByField(GridFieldPB field) = _GroupByField;
const factory BoardEvent.didReceiveGridUpdate(
GridPB grid,
) = _DidReceiveGridUpdate;
@ -166,19 +154,17 @@ class BoardState with _$BoardState {
const factory BoardState({
required String gridId,
required Option<GridPB> grid,
required GridFieldEquatable fields,
required Option<GridFieldPB> groupField,
required List<GridRowInfo> rowInfos,
required GridLoadingState loadingState,
required GridRowChangeReason reason,
}) = _BoardState;
factory BoardState.initial(String gridId) => BoardState(
fields: GridFieldEquatable(UnmodifiableListView([])),
rowInfos: [],
groupField: none(),
grid: none(),
gridId: gridId,
loadingState: const _Loading(),
reason: const InitialListState(),
);
}

View File

@ -23,7 +23,10 @@ class BoardPluginBuilder implements PluginBuilder {
PluginType get pluginType => DefaultPlugin.board.type();
@override
ViewDataType get dataType => ViewDataType.Grid;
ViewDataTypePB get dataType => ViewDataTypePB.Database;
@override
SubViewDataTypePB get subDataType => SubViewDataTypePB.Board;
}
class BoardPluginConfig implements PluginConfig {

View File

@ -1,15 +1,17 @@
// ignore_for_file: unused_field
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
import 'package:appflowy_board/appflowy_board.dart';
import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../application/board_bloc.dart';
import 'card.dart';
class BoardPage extends StatelessWidget {
final ViewPB view;
const BoardPage({required this.view, Key? key}) : super(key: key);
BoardPage({required this.view, Key? key}) : super(key: ValueKey(view.id));
@override
Widget build(BuildContext context) {
@ -53,12 +55,7 @@ class BoardContent extends StatelessWidget {
dataController: context.read<BoardBloc>().boardDataController,
headerBuilder: _buildHeader,
footBuilder: _buildFooter,
cardBuilder: (context, item) {
return AppFlowyColumnItemCard(
key: ObjectKey(item),
child: _buildCard(item),
);
},
cardBuilder: _buildCard,
columnConstraints: const BoxConstraints.tightFor(width: 240),
config: BoardConfig(
columnBackgroundColor: HexColor.fromHex('#F7F8FC'),
@ -90,42 +87,12 @@ class BoardContent extends StatelessWidget {
);
}
Widget _buildCard(ColumnItem item) {
if (item is TextItem) {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Text(item.s),
),
);
}
if (item is RichTextItem) {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.title,
style: const TextStyle(fontSize: 14),
textAlign: TextAlign.left,
),
const SizedBox(height: 10),
Text(
item.subtitle,
style: const TextStyle(fontSize: 12, color: Colors.grey),
)
],
),
),
);
}
throw UnimplementedError();
Widget _buildCard(BuildContext context, ColumnItem item) {
final rowInfo = item as GridRowInfo;
return AppFlowyColumnItemCard(
key: ObjectKey(item),
child: BoardCard(rowInfo: rowInfo),
);
}
}

View File

@ -0,0 +1,13 @@
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
import 'package:flutter/material.dart';
class BoardCard extends StatelessWidget {
final GridRowInfo rowInfo;
const BoardCard({required this.rowInfo, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(child: Text('1234'));
}
}

View File

@ -1,4 +1,4 @@
library docuemnt_plugin;
library document_plugin;
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/startup/plugin/plugin.dart';
@ -42,10 +42,10 @@ class DocumentPluginBuilder extends PluginBuilder {
String get menuName => LocaleKeys.document_menuName.tr();
@override
PluginType get pluginType => DefaultPlugin.quill.type();
PluginType get pluginType => DefaultPlugin.editor.type();
@override
ViewDataType get dataType => ViewDataType.TextBlock;
ViewDataTypePB get dataType => ViewDataTypePB.TextBlock;
}
class DocumentPlugin implements Plugin {

View File

@ -25,7 +25,10 @@ class GridPluginBuilder implements PluginBuilder {
PluginType get pluginType => DefaultPlugin.grid.type();
@override
ViewDataType get dataType => ViewDataType.Grid;
ViewDataTypePB get dataType => ViewDataTypePB.Database;
@override
SubViewDataTypePB? get subDataType => SubViewDataTypePB.Grid;
}
class GridPluginConfig implements PluginConfig {

View File

@ -10,7 +10,7 @@ import 'package:flutter/widgets.dart';
export "./src/sandbox.dart";
enum DefaultPlugin {
quill,
editor,
blank,
trash,
grid,
@ -20,7 +20,7 @@ enum DefaultPlugin {
extension FlowyDefaultPluginExt on DefaultPlugin {
int type() {
switch (this) {
case DefaultPlugin.quill:
case DefaultPlugin.editor:
return 0;
case DefaultPlugin.blank:
return 1;
@ -35,7 +35,6 @@ extension FlowyDefaultPluginExt on DefaultPlugin {
}
typedef PluginType = int;
typedef PluginDataType = ViewDataType;
typedef PluginId = String;
abstract class Plugin {
@ -55,7 +54,9 @@ abstract class PluginBuilder {
PluginType get pluginType;
ViewDataType get dataType => ViewDataType.TextBlock;
ViewDataTypePB get dataType => ViewDataTypePB.TextBlock;
SubViewDataTypePB? get subDataType => null;
}
abstract class PluginConfig {

View File

@ -86,6 +86,7 @@ class AppBloc extends Bloc<AppEvent, AppState> {
desc: value.desc,
dataType: value.dataType,
pluginType: value.pluginType,
subDataType: value.subDataType,
);
viewOrFailed.fold(
(view) => emit(state.copyWith(
@ -138,7 +139,8 @@ class AppEvent with _$AppEvent {
const factory AppEvent.createView(
String name,
String desc,
PluginDataType dataType,
ViewDataTypePB dataType,
SubViewDataTypePB? subDataType,
PluginType pluginType,
) = CreateView;
const factory AppEvent.delete() = Delete;

View File

@ -24,16 +24,21 @@ class AppService {
required String appId,
required String name,
required String desc,
required PluginDataType dataType,
required ViewDataTypePB dataType,
required PluginType pluginType,
SubViewDataTypePB? subDataType,
}) {
final payload = CreateViewPayloadPB.create()
var payload = CreateViewPayloadPB.create()
..belongToId = appId
..name = name
..desc = desc
..dataType = dataType
..pluginType = pluginType;
if (subDataType != null) {
payload.subDataType = subDataType;
}
return FolderEventCreateView(payload).send();
}

View File

@ -49,7 +49,9 @@ class MenuAppHeader extends StatelessWidget {
height: MenuAppSizes.headerHeight,
child: InkWell(
onTap: () {
ExpandableController.of(context, rebuildOnChange: false, required: true)?.toggle();
ExpandableController.of(context,
rebuildOnChange: false, required: true)
?.toggle();
},
child: ExpandableIcon(
theme: ExpandableThemeData(
@ -68,18 +70,23 @@ class MenuAppHeader extends StatelessWidget {
Widget _renderTitle(BuildContext context, AppTheme theme) {
return Expanded(
child: BlocListener<AppBloc, AppState>(
listenWhen: (p, c) => (p.latestCreatedView == null && c.latestCreatedView != null),
listenWhen: (p, c) =>
(p.latestCreatedView == null && c.latestCreatedView != null),
listener: (context, state) {
final expandableController = ExpandableController.of(context, rebuildOnChange: false, required: true)!;
final expandableController = ExpandableController.of(context,
rebuildOnChange: false, required: true)!;
if (!expandableController.expanded) {
expandableController.toggle();
}
},
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => ExpandableController.of(context, rebuildOnChange: false, required: true)?.toggle(),
onTap: () => ExpandableController.of(context,
rebuildOnChange: false, required: true)
?.toggle(),
onSecondaryTap: () {
final actionList = AppDisclosureActionSheet(onSelected: (action) => _handleAction(context, action));
final actionList = AppDisclosureActionSheet(
onSelected: (action) => _handleAction(context, action));
actionList.show(
context,
anchorDirection: AnchorDirection.bottomWithCenterAligned,
@ -107,6 +114,7 @@ class MenuAppHeader extends StatelessWidget {
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
"",
pluginBuilder.dataType,
pluginBuilder.subDataType,
pluginBuilder.pluginType,
));
},

View File

@ -22,7 +22,7 @@ pub struct ViewPB {
pub name: String,
#[pb(index = 4)]
pub data_type: ViewDataType,
pub data_type: ViewDataTypePB,
#[pb(index = 5)]
pub modified_time: i64,
@ -49,31 +49,37 @@ impl std::convert::From<ViewRevision> for ViewPB {
}
#[derive(Eq, PartialEq, Hash, Debug, ProtoBuf_Enum, Clone)]
pub enum ViewDataType {
pub enum ViewDataTypePB {
TextBlock = 0,
Grid = 1,
Database = 1,
}
impl std::default::Default for ViewDataType {
#[derive(Eq, PartialEq, Hash, Debug, ProtoBuf_Enum, Clone)]
pub enum SubViewDataTypePB {
Grid = 0,
Board = 1,
}
impl std::default::Default for ViewDataTypePB {
fn default() -> Self {
ViewDataTypeRevision::default().into()
}
}
impl std::convert::From<ViewDataTypeRevision> for ViewDataType {
impl std::convert::From<ViewDataTypeRevision> for ViewDataTypePB {
fn from(rev: ViewDataTypeRevision) -> Self {
match rev {
ViewDataTypeRevision::TextBlock => ViewDataType::TextBlock,
ViewDataTypeRevision::Grid => ViewDataType::Grid,
ViewDataTypeRevision::TextBlock => ViewDataTypePB::TextBlock,
ViewDataTypeRevision::Database => ViewDataTypePB::Database,
}
}
}
impl std::convert::From<ViewDataType> for ViewDataTypeRevision {
fn from(ty: ViewDataType) -> Self {
impl std::convert::From<ViewDataTypePB> for ViewDataTypeRevision {
fn from(ty: ViewDataTypePB) -> Self {
match ty {
ViewDataType::TextBlock => ViewDataTypeRevision::TextBlock,
ViewDataType::Grid => ViewDataTypeRevision::Grid,
ViewDataTypePB::TextBlock => ViewDataTypeRevision::TextBlock,
ViewDataTypePB::Database => ViewDataTypeRevision::Database,
}
}
}
@ -113,12 +119,15 @@ pub struct CreateViewPayloadPB {
pub thumbnail: Option<String>,
#[pb(index = 5)]
pub data_type: ViewDataType,
pub data_type: ViewDataTypePB,
#[pb(index = 6)]
pub plugin_type: i32,
#[pb(index = 6, one_of)]
pub sub_data_type: Option<SubViewDataTypePB>,
#[pb(index = 7)]
pub plugin_type: i32,
#[pb(index = 8)]
pub data: Vec<u8>,
}
@ -128,7 +137,8 @@ pub struct CreateViewParams {
pub name: String,
pub desc: String,
pub thumbnail: String,
pub data_type: ViewDataType,
pub data_type: ViewDataTypePB,
pub sub_data_type: Option<SubViewDataTypePB>,
pub view_id: String,
pub data: Vec<u8>,
pub plugin_type: i32,
@ -151,6 +161,7 @@ impl TryInto<CreateViewParams> for CreateViewPayloadPB {
name,
desc: self.desc,
data_type: self.data_type,
sub_data_type: self.sub_data_type,
thumbnail,
view_id,
data: self.data,

View File

@ -1,4 +1,4 @@
use crate::entities::{RepeatedViewPB, ViewDataType};
use crate::entities::{RepeatedViewPB, ViewDataTypePB};
use flowy_derive::ProtoBuf;
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
@ -16,7 +16,7 @@ pub struct ViewInfoPB {
pub desc: String,
#[pb(index = 5)]
pub data_type: ViewDataType,
pub data_type: ViewDataTypePB,
#[pb(index = 6)]
pub belongings: RepeatedViewPB,

View File

@ -1,4 +1,5 @@
use crate::entities::view::ViewDataType;
use crate::entities::view::ViewDataTypePB;
use crate::entities::SubViewDataTypePB;
use crate::services::folder_editor::FolderRevisionCompactor;
use crate::{
dart_notification::{send_dart_notification, FolderNotification},
@ -221,7 +222,7 @@ impl DefaultFolderBuilder {
};
let _ = view_controller.set_latest_view(&view.id);
let _ = view_controller
.create_view(&view.id, ViewDataType::TextBlock, Bytes::from(view_data))
.create_view(&view.id, ViewDataTypePB::TextBlock, Bytes::from(view_data))
.await?;
}
}
@ -256,7 +257,12 @@ pub trait ViewDataProcessor {
fn get_delta_data(&self, view_id: &str) -> FutureResult<Bytes, FlowyError>;
fn create_default_view(&self, user_id: &str, view_id: &str) -> FutureResult<Bytes, FlowyError>;
fn create_default_view(
&self,
user_id: &str,
view_id: &str,
sub_data_type: Option<SubViewDataTypePB>,
) -> FutureResult<Bytes, FlowyError>;
fn create_view_from_delta_data(
&self,
@ -265,7 +271,7 @@ pub trait ViewDataProcessor {
data: Vec<u8>,
) -> FutureResult<Bytes, FlowyError>;
fn data_type(&self) -> ViewDataType;
fn data_type(&self) -> ViewDataTypePB;
}
pub type ViewDataProcessorMap = Arc<HashMap<ViewDataType, Arc<dyn ViewDataProcessor + Send + Sync>>>;
pub type ViewDataProcessorMap = Arc<HashMap<ViewDataTypePB, Arc<dyn ViewDataProcessor + Send + Sync>>>;

View File

@ -88,7 +88,7 @@ impl ViewTable {
pub fn new(view_rev: ViewRevision) -> Self {
let data_type = match view_rev.data_type {
ViewDataTypeRevision::TextBlock => SqlViewDataType::Block,
ViewDataTypeRevision::Grid => SqlViewDataType::Grid,
ViewDataTypeRevision::Database => SqlViewDataType::Grid,
};
ViewTable {
@ -111,7 +111,7 @@ impl std::convert::From<ViewTable> for ViewRevision {
fn from(table: ViewTable) -> Self {
let data_type = match table.view_type {
SqlViewDataType::Block => ViewDataTypeRevision::TextBlock,
SqlViewDataType::Grid => ViewDataTypeRevision::Grid,
SqlViewDataType::Grid => ViewDataTypeRevision::Database,
};
ViewRevision {

View File

@ -1,5 +1,5 @@
pub use crate::entities::view::ViewDataType;
use crate::entities::ViewInfoPB;
pub use crate::entities::view::ViewDataTypePB;
use crate::entities::{SubViewDataTypePB, ViewInfoPB};
use crate::manager::{ViewDataProcessor, ViewDataProcessorMap};
use crate::{
dart_notification::{send_dart_notification, FolderNotification},
@ -61,7 +61,9 @@ impl ViewController {
let processor = self.get_data_processor(params.data_type.clone())?;
let user_id = self.user.user_id()?;
if params.data.is_empty() {
let view_data = processor.create_default_view(&user_id, &params.view_id).await?;
let view_data = processor
.create_default_view(&user_id, &params.view_id, params.sub_data_type.clone())
.await?;
params.data = view_data.to_vec();
} else {
let delta_data = processor
@ -81,7 +83,7 @@ impl ViewController {
pub(crate) async fn create_view(
&self,
view_id: &str,
data_type: ViewDataType,
data_type: ViewDataTypePB,
delta_data: Bytes,
) -> Result<(), FlowyError> {
if delta_data.is_empty() {
@ -217,6 +219,7 @@ impl ViewController {
desc: view_rev.desc,
thumbnail: view_rev.thumbnail,
data_type: view_rev.data_type.into(),
sub_data_type: None,
data: delta_bytes.to_vec(),
view_id: gen_view_id(),
plugin_type: view_rev.plugin_type,
@ -364,7 +367,7 @@ impl ViewController {
}
#[inline]
fn get_data_processor<T: Into<ViewDataType>>(
fn get_data_processor<T: Into<ViewDataTypePB>>(
&self,
data_type: T,
) -> FlowyResult<Arc<dyn ViewDataProcessor + Send + Sync>> {
@ -452,7 +455,7 @@ async fn handle_trash_event(
fn get_data_processor(
data_processors: ViewDataProcessorMap,
data_type: &ViewDataType,
data_type: &ViewDataTypePB,
) -> FlowyResult<Arc<dyn ViewDataProcessor + Send + Sync>> {
match data_processors.get(data_type) {
None => Err(FlowyError::internal().context(format!(

View File

@ -1,5 +1,5 @@
use crate::script::{invalid_workspace_name_test_case, FolderScript::*, FolderTest};
use flowy_folder::entities::view::ViewDataType;
use flowy_folder::entities::view::ViewDataTypePB;
use flowy_folder::entities::workspace::CreateWorkspacePayloadPB;
use flowy_revision::disk::RevisionState;
@ -134,12 +134,12 @@ async fn app_create_with_view() {
CreateView {
name: "View A".to_owned(),
desc: "View A description".to_owned(),
data_type: ViewDataType::TextBlock,
data_type: ViewDataTypePB::TextBlock,
},
CreateView {
name: "Grid".to_owned(),
desc: "Grid description".to_owned(),
data_type: ViewDataType::Grid,
data_type: ViewDataTypePB::Database,
},
ReadApp(app.id),
])
@ -198,12 +198,12 @@ async fn view_delete_all() {
CreateView {
name: "View A".to_owned(),
desc: "View A description".to_owned(),
data_type: ViewDataType::TextBlock,
data_type: ViewDataTypePB::TextBlock,
},
CreateView {
name: "Grid".to_owned(),
desc: "Grid description".to_owned(),
data_type: ViewDataType::Grid,
data_type: ViewDataTypePB::Database,
},
ReadApp(app.id.clone()),
])
@ -231,7 +231,7 @@ async fn view_delete_all_permanent() {
CreateView {
name: "View A".to_owned(),
desc: "View A description".to_owned(),
data_type: ViewDataType::TextBlock,
data_type: ViewDataTypePB::TextBlock,
},
ReadApp(app.id.clone()),
])
@ -330,7 +330,7 @@ async fn folder_sync_revision_with_new_view() {
CreateView {
name: view_name.clone(),
desc: view_desc.clone(),
data_type: ViewDataType::TextBlock,
data_type: ViewDataTypePB::TextBlock,
},
AssertCurrentRevId(3),
AssertNextSyncRevId(Some(3)),

View File

@ -9,7 +9,7 @@ use flowy_folder::entities::{
use flowy_folder::entities::{
app::{AppPB, RepeatedAppPB},
trash::TrashPB,
view::{RepeatedViewPB, ViewDataType, ViewPB},
view::{RepeatedViewPB, ViewDataTypePB, ViewPB},
workspace::WorkspacePB,
};
use flowy_folder::event_map::FolderEvent::*;
@ -51,7 +51,7 @@ pub enum FolderScript {
CreateView {
name: String,
desc: String,
data_type: ViewDataType,
data_type: ViewDataTypePB,
},
AssertView(ViewPB),
ReadView(String),
@ -98,7 +98,7 @@ impl FolderTest {
&app.id,
"Folder View",
"Folder test view",
ViewDataType::TextBlock,
ViewDataTypePB::TextBlock,
)
.await;
app.belongings = RepeatedViewPB {
@ -346,7 +346,13 @@ pub async fn delete_app(sdk: &FlowySDKTest, app_id: &str) {
.await;
}
pub async fn create_view(sdk: &FlowySDKTest, app_id: &str, name: &str, desc: &str, data_type: ViewDataType) -> ViewPB {
pub async fn create_view(
sdk: &FlowySDKTest,
app_id: &str,
name: &str,
desc: &str,
data_type: ViewDataTypePB,
) -> ViewPB {
let request = CreateViewPayloadPB {
belong_to_id: app_id.to_string(),
name: name.to_string(),

View File

@ -82,7 +82,7 @@ impl_into_box_type_option_builder!(SingleSelectTypeOptionBuilder);
impl_builder_from_json_str_and_from_bytes!(SingleSelectTypeOptionBuilder, SingleSelectTypeOptionPB);
impl SingleSelectTypeOptionBuilder {
pub fn option(mut self, opt: SelectOptionPB) -> Self {
pub fn add_option(mut self, opt: SelectOptionPB) -> Self {
self.0.options.push(opt);
self
}
@ -113,9 +113,9 @@ mod tests {
let facebook_option = SelectOptionPB::new("Facebook");
let twitter_option = SelectOptionPB::new("Twitter");
let single_select = SingleSelectTypeOptionBuilder::default()
.option(google_option.clone())
.option(facebook_option.clone())
.option(twitter_option);
.add_option(google_option.clone())
.add_option(facebook_option.clone())
.add_option(twitter_option);
let field_rev = FieldBuilder::new(single_select)
.name("Platform")

View File

@ -133,7 +133,7 @@ mod tests {
// Single select
let done_option = SelectOptionPB::new("Done");
let done_option_id = done_option.id.clone();
let single_select = SingleSelectTypeOptionBuilder::default().option(done_option.clone());
let single_select = SingleSelectTypeOptionBuilder::default().add_option(done_option.clone());
let single_select_field_rev = FieldBuilder::new(single_select).build();
assert_eq!(

View File

@ -272,7 +272,7 @@ impl GridRevisionEditor {
let block_id = self.block_id().await?;
// insert empty row below the row whose id is upper_row_id
let row_rev = RowRevisionBuilder::new(&field_revs).build(&block_id);
let row_rev = RowRevisionBuilder::new(&block_id, &field_revs).build();
let row_order = GridRowPB::from(&row_rev);
// insert the row

View File

@ -7,12 +7,13 @@ use std::collections::HashMap;
use std::sync::Arc;
pub struct RowRevisionBuilder<'a> {
block_id: String,
field_rev_map: HashMap<&'a String, Arc<FieldRevision>>,
payload: CreateRowRevisionPayload,
}
impl<'a> RowRevisionBuilder<'a> {
pub fn new(fields: &'a [Arc<FieldRevision>]) -> Self {
pub fn new(block_id: &str, fields: &'a [Arc<FieldRevision>]) -> Self {
let field_rev_map = fields
.iter()
.map(|field| (&field.id, field.clone()))
@ -25,7 +26,13 @@ impl<'a> RowRevisionBuilder<'a> {
visibility: true,
};
Self { field_rev_map, payload }
let block_id = block_id.to_string();
Self {
block_id,
field_rev_map,
payload,
}
}
pub fn insert_cell(&mut self, field_id: &str, data: String) -> FlowyResult<()> {
@ -43,18 +50,18 @@ impl<'a> RowRevisionBuilder<'a> {
}
}
pub fn insert_select_option_cell(&mut self, field_id: &str, data: String) -> FlowyResult<()> {
pub fn insert_select_option_cell(mut self, field_id: &str, data: String) -> Self {
match self.field_rev_map.get(&field_id.to_owned()) {
None => {
let msg = format!("Invalid field_id: {}", field_id);
Err(FlowyError::internal().context(msg))
tracing::warn!("Invalid field_id: {}", field_id);
self
}
Some(field_rev) => {
let cell_data = SelectOptionCellChangeset::from_insert(&data).to_str();
let data = apply_cell_data_changeset(cell_data, None, field_rev)?;
let data = apply_cell_data_changeset(cell_data, None, field_rev).unwrap();
let cell = CellRevision::new(data);
self.payload.cell_by_field_id.insert(field_id.to_owned(), cell);
Ok(())
self
}
}
}
@ -71,10 +78,10 @@ impl<'a> RowRevisionBuilder<'a> {
self
}
pub fn build(self, block_id: &str) -> RowRevision {
pub fn build(self) -> RowRevision {
RowRevision {
id: self.payload.row_id,
block_id: block_id.to_owned(),
block_id: self.block_id,
cells: self.payload.cell_by_field_id,
height: self.payload.height,
visibility: self.payload.visibility,

View File

@ -1,5 +1,6 @@
use crate::entities::FieldType;
use crate::services::field::*;
use crate::services::row::RowRevisionBuilder;
use flowy_grid_data_model::revision::BuildGridContext;
use flowy_sync::client_grid::GridBuilder;
@ -30,3 +31,47 @@ pub fn make_default_grid() -> BuildGridContext {
grid_builder.add_empty_row();
grid_builder.build()
}
pub fn make_default_board() -> BuildGridContext {
let mut grid_builder = GridBuilder::new();
// text
let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default())
.name("Name")
.visibility(true)
.primary(true)
.build();
grid_builder.add_field(text_field);
// single select
let in_progress_option = SelectOptionPB::new("In progress");
let not_started_option = SelectOptionPB::new("Not started");
let done_option = SelectOptionPB::new("Done");
let single_select = SingleSelectTypeOptionBuilder::default()
.add_option(not_started_option.clone())
.add_option(in_progress_option.clone())
.add_option(done_option.clone());
let single_select_field = FieldBuilder::new(single_select).name("Status").visibility(true).build();
let single_select_field_id = single_select_field.id.clone();
grid_builder.add_field(single_select_field);
let field_revs = grid_builder.field_revs();
let block_id = grid_builder.block_id();
// rows
let row_1 = RowRevisionBuilder::new(block_id, field_revs)
.insert_select_option_cell(&single_select_field_id, not_started_option.id.clone())
.build();
grid_builder.add_row(row_1);
let row_2 = RowRevisionBuilder::new(block_id, field_revs)
.insert_select_option_cell(&single_select_field_id, not_started_option.id.clone())
.build();
grid_builder.add_row(row_2);
let row_3 = RowRevisionBuilder::new(block_id, field_revs)
.insert_select_option_cell(&single_select_field_id, not_started_option.id.clone())
.build();
grid_builder.add_row(row_3);
grid_builder.build()
}

View File

@ -18,7 +18,7 @@ pub struct GridRowTestBuilder<'a> {
impl<'a> GridRowTestBuilder<'a> {
pub fn new(block_id: &str, field_revs: &'a [Arc<FieldRevision>]) -> Self {
assert_eq!(field_revs.len(), FieldType::COUNT);
let inner_builder = RowRevisionBuilder::new(field_revs);
let inner_builder = RowRevisionBuilder::new(block_id, field_revs);
Self {
block_id: block_id.to_owned(),
field_revs,
@ -77,8 +77,7 @@ impl<'a> GridRowTestBuilder<'a> {
let type_option = SingleSelectTypeOptionPB::from(&single_select_field);
let option = f(type_option.options);
self.inner_builder
.insert_select_option_cell(&single_select_field.id, option.id)
.unwrap();
.insert_select_option_cell(&single_select_field.id, option.id);
single_select_field.id.clone()
}
@ -96,8 +95,7 @@ impl<'a> GridRowTestBuilder<'a> {
.collect::<Vec<_>>()
.join(SELECTION_IDS_SEPARATOR);
self.inner_builder
.insert_select_option_cell(&multi_select_field.id, ops_ids)
.unwrap();
.insert_select_option_cell(&multi_select_field.id, ops_ids);
multi_select_field.id.clone()
}
@ -115,7 +113,7 @@ impl<'a> GridRowTestBuilder<'a> {
}
pub fn build(self) -> RowRevision {
self.inner_builder.build(&self.block_id)
self.inner_builder.build()
}
}

View File

@ -39,8 +39,8 @@ pub fn create_text_field(grid_id: &str) -> (InsertFieldParams, FieldRevision) {
pub fn create_single_select_field(grid_id: &str) -> (InsertFieldParams, FieldRevision) {
let single_select = SingleSelectTypeOptionBuilder::default()
.option(SelectOptionPB::new("Done"))
.option(SelectOptionPB::new("Progress"));
.add_option(SelectOptionPB::new("Done"))
.add_option(SelectOptionPB::new("Progress"));
let field_rev = FieldBuilder::new(single_select).name("Name").visibility(true).build();
let cloned_field_rev = field_rev.clone();

View File

@ -138,9 +138,9 @@ fn make_test_grid() -> BuildGridContext {
FieldType::SingleSelect => {
// Single Select
let single_select = SingleSelectTypeOptionBuilder::default()
.option(SelectOptionPB::new(COMPLETED))
.option(SelectOptionPB::new(PLANNED))
.option(SelectOptionPB::new(PAUSED));
.add_option(SelectOptionPB::new(COMPLETED))
.add_option(SelectOptionPB::new(PLANNED))
.add_option(SelectOptionPB::new(PAUSED));
let single_select_field = FieldBuilder::new(single_select).name("Status").visibility(true).build();
grid_builder.add_field(single_select_field);
}

View File

@ -1,6 +1,6 @@
use bytes::Bytes;
use flowy_database::ConnectionPool;
use flowy_folder::entities::ViewDataType;
use flowy_folder::entities::{SubViewDataTypePB, ViewDataTypePB};
use flowy_folder::manager::{ViewDataProcessor, ViewDataProcessorMap};
use flowy_folder::{
errors::{internal_error, FlowyError},
@ -8,7 +8,7 @@ use flowy_folder::{
manager::FolderManager,
};
use flowy_grid::manager::{make_grid_view_data, GridManager};
use flowy_grid::util::make_default_grid;
use flowy_grid::util::{make_default_board, make_default_grid};
use flowy_grid_data_model::revision::BuildGridContext;
use flowy_net::ClientServerConfiguration;
use flowy_net::{
@ -66,7 +66,7 @@ fn make_view_data_processor(
text_block_manager: Arc<TextBlockManager>,
grid_manager: Arc<GridManager>,
) -> ViewDataProcessorMap {
let mut map: HashMap<ViewDataType, Arc<dyn ViewDataProcessor + Send + Sync>> = HashMap::new();
let mut map: HashMap<ViewDataTypePB, Arc<dyn ViewDataProcessor + Send + Sync>> = HashMap::new();
let block_data_impl = TextBlockViewDataProcessor(text_block_manager);
map.insert(block_data_impl.data_type(), Arc::new(block_data_impl));
@ -180,7 +180,12 @@ impl ViewDataProcessor for TextBlockViewDataProcessor {
})
}
fn create_default_view(&self, user_id: &str, view_id: &str) -> FutureResult<Bytes, FlowyError> {
fn create_default_view(
&self,
user_id: &str,
view_id: &str,
_sub_data_type: Option<SubViewDataTypePB>,
) -> FutureResult<Bytes, FlowyError> {
let user_id = user_id.to_string();
let view_id = view_id.to_string();
let manager = self.0.clone();
@ -203,8 +208,8 @@ impl ViewDataProcessor for TextBlockViewDataProcessor {
FutureResult::new(async move { Ok(Bytes::from(data)) })
}
fn data_type(&self) -> ViewDataType {
ViewDataType::TextBlock
fn data_type(&self) -> ViewDataTypePB {
ViewDataTypePB::TextBlock
}
}
@ -252,8 +257,16 @@ impl ViewDataProcessor for GridViewDataProcessor {
})
}
fn create_default_view(&self, user_id: &str, view_id: &str) -> FutureResult<Bytes, FlowyError> {
let build_context = make_default_grid();
fn create_default_view(
&self,
user_id: &str,
view_id: &str,
sub_data_type: Option<SubViewDataTypePB>,
) -> FutureResult<Bytes, FlowyError> {
let build_context = match sub_data_type.unwrap() {
SubViewDataTypePB::Grid => make_default_grid(),
SubViewDataTypePB::Board => make_default_board(),
};
let user_id = user_id.to_string();
let view_id = view_id.to_string();
let grid_manager = self.0.clone();
@ -278,7 +291,7 @@ impl ViewDataProcessor for GridViewDataProcessor {
})
}
fn data_type(&self) -> ViewDataType {
ViewDataType::Grid
fn data_type(&self) -> ViewDataTypePB {
ViewDataTypePB::Database
}
}

View File

@ -25,7 +25,7 @@ pub struct ViewTest {
impl ViewTest {
#[allow(dead_code)]
pub async fn new(sdk: &FlowySDKTest, data_type: ViewDataType, data: Vec<u8>) -> Self {
pub async fn new(sdk: &FlowySDKTest, data_type: ViewDataTypePB, data: Vec<u8>) -> Self {
let workspace = create_workspace(sdk, "Workspace", "").await;
open_workspace(sdk, &workspace.id).await;
let app = create_app(sdk, "App", "AppFlowy GitHub Project", &workspace.id).await;
@ -39,11 +39,11 @@ impl ViewTest {
}
pub async fn new_grid_view(sdk: &FlowySDKTest, data: Vec<u8>) -> Self {
Self::new(sdk, ViewDataType::Grid, data).await
Self::new(sdk, ViewDataTypePB::Database, data).await
}
pub async fn new_text_block_view(sdk: &FlowySDKTest) -> Self {
Self::new(sdk, ViewDataType::TextBlock, vec![]).await
Self::new(sdk, ViewDataTypePB::TextBlock, vec![]).await
}
}
@ -90,7 +90,7 @@ async fn create_app(sdk: &FlowySDKTest, name: &str, desc: &str, workspace_id: &s
app
}
async fn create_view(sdk: &FlowySDKTest, app_id: &str, data_type: ViewDataType, data: Vec<u8>) -> ViewPB {
async fn create_view(sdk: &FlowySDKTest, app_id: &str, data_type: ViewDataTypePB, data: Vec<u8>) -> ViewPB {
let request = CreateViewPayloadPB {
belong_to_id: app_id.to_string(),
name: "View A".to_string(),

View File

@ -53,7 +53,7 @@ impl std::convert::From<ViewRevision> for TrashRevision {
#[repr(u8)]
pub enum ViewDataTypeRevision {
TextBlock = 0,
Grid = 1,
Database = 1,
}
impl std::default::Default for ViewDataTypeRevision {