feat: switch database layout (#2677)

* chore: rename update at and create at

* chore: support switching view layout

* chore: implement ui

* chore: update layout type

* refactor: board/calendar/grid setting button

* chore: update UI after switch to other layout type

* fix: no date display in calendar

* chore: update patch

* chore: fix create ref view in document

* chore: fix flutter analyze

* ci: warnings

* chore: rename board and grid keys

* fix: calendar row event update

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
This commit is contained in:
Nathan.fooo
2023-06-01 20:23:27 +08:00
committed by GitHub
parent 2ef72f3203
commit 33e0f8d26d
98 changed files with 1600 additions and 1122 deletions

View File

@ -134,3 +134,18 @@ pub struct MoveCalendarEventPB {
#[pb(index = 2)]
pub timestamp: i64,
}
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct NoDateCalendarEventPB {
#[pb(index = 1)]
pub row_id: String,
#[pb(index = 2)]
pub title: String,
}
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct RepeatedNoDateCalendarEventPB {
#[pb(index = 1)]
pub items: Vec<NoDateCalendarEventPB>,
}

View File

@ -3,10 +3,11 @@ use collab_database::user::DatabaseRecord;
use collab_database::views::DatabaseLayout;
use flowy_derive::ProtoBuf;
use flowy_error::ErrorCode;
use flowy_error::{ErrorCode, FlowyError};
use crate::entities::parser::NotEmptyStr;
use crate::entities::{DatabaseLayoutPB, FieldIdPB, RowPB};
use crate::services::database::CreateDatabaseViewParams;
/// [DatabasePB] describes how many fields and blocks the grid has
#[derive(Debug, Clone, Default, ProtoBuf)]
@ -19,12 +20,34 @@ pub struct DatabasePB {
#[pb(index = 3)]
pub rows: Vec<RowPB>,
#[pb(index = 4)]
pub layout_type: DatabaseLayoutPB,
}
#[derive(ProtoBuf, Default)]
pub struct CreateDatabasePayloadPB {
pub struct CreateDatabaseViewPayloadPB {
#[pb(index = 1)]
pub name: String,
#[pb(index = 2)]
pub view_id: String,
#[pb(index = 3)]
pub layout_type: DatabaseLayoutPB,
}
impl TryInto<CreateDatabaseViewParams> for CreateDatabaseViewPayloadPB {
type Error = FlowyError;
fn try_into(self) -> Result<CreateDatabaseViewParams, Self::Error> {
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseViewIdIsEmpty)?;
Ok(CreateDatabaseViewParams {
name: self.name,
view_id: view_id.0,
layout_type: self.layout_type.into(),
})
}
}
#[derive(Clone, ProtoBuf, Default, Debug)]
@ -198,7 +221,7 @@ impl TryInto<DatabaseGroupIdParams> for DatabaseGroupIdPB {
}
}
#[derive(Clone, ProtoBuf, Default, Debug)]
pub struct DatabaseLayoutIdPB {
pub struct DatabaseLayoutMetaPB {
#[pb(index = 1)]
pub view_id: String,
@ -207,18 +230,18 @@ pub struct DatabaseLayoutIdPB {
}
#[derive(Clone, Debug)]
pub struct DatabaseLayoutId {
pub struct DatabaseLayoutMeta {
pub view_id: String,
pub layout: DatabaseLayout,
}
impl TryInto<DatabaseLayoutId> for DatabaseLayoutIdPB {
impl TryInto<DatabaseLayoutMeta> for DatabaseLayoutMetaPB {
type Error = ErrorCode;
fn try_into(self) -> Result<DatabaseLayoutId, Self::Error> {
fn try_into(self) -> Result<DatabaseLayoutMeta, Self::Error> {
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseViewIdIsEmpty)?;
let layout = self.layout.into();
Ok(DatabaseLayoutId {
Ok(DatabaseLayoutMeta {
view_id: view_id.0,
layout,
})

View File

@ -121,7 +121,7 @@ pub struct UpdatedRowPB {
#[pb(index = 1)]
pub row: RowPB,
// represents as the cells that were updated in this row.
// Indicates the field ids of the cells that were updated in this row.
#[pb(index = 2)]
pub field_ids: Vec<String>,
}

View File

@ -18,10 +18,10 @@ use crate::services::setting::CalendarLayoutSetting;
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
pub struct DatabaseViewSettingPB {
#[pb(index = 1)]
pub current_layout: DatabaseLayoutPB,
pub layout_type: DatabaseLayoutPB,
#[pb(index = 2)]
pub layout_setting: LayoutSettingPB,
pub layout_setting: DatabaseLayoutSettingPB,
#[pb(index = 3)]
pub filters: RepeatedFilterPB,
@ -72,8 +72,8 @@ pub struct DatabaseSettingChangesetPB {
#[pb(index = 1)]
pub view_id: String,
#[pb(index = 2)]
pub layout_type: DatabaseLayoutPB,
#[pb(index = 2, one_of)]
pub layout_type: Option<DatabaseLayoutPB>,
#[pb(index = 3, one_of)]
pub update_filter: Option<UpdateFilterPayloadPB>,
@ -121,7 +121,7 @@ impl TryInto<DatabaseSettingChangesetParams> for DatabaseSettingChangesetPB {
Ok(DatabaseSettingChangesetParams {
view_id,
layout_type: self.layout_type.into(),
layout_type: self.layout_type.map(|ty| ty.into()),
insert_filter,
delete_filter,
alert_sort,
@ -132,7 +132,7 @@ impl TryInto<DatabaseSettingChangesetParams> for DatabaseSettingChangesetPB {
pub struct DatabaseSettingChangesetParams {
pub view_id: String,
pub layout_type: DatabaseLayout,
pub layout_type: Option<DatabaseLayout>,
pub insert_filter: Option<UpdateFilterParams>,
pub delete_filter: Option<DeleteFilterParams>,
pub alert_sort: Option<UpdateSortParams>,
@ -146,19 +146,24 @@ impl DatabaseSettingChangesetParams {
}
#[derive(Debug, Eq, PartialEq, Default, ProtoBuf, Clone)]
pub struct LayoutSettingPB {
#[pb(index = 1, one_of)]
pub struct DatabaseLayoutSettingPB {
#[pb(index = 1)]
pub layout_type: DatabaseLayoutPB,
#[pb(index = 2, one_of)]
pub calendar: Option<CalendarLayoutSettingPB>,
}
#[derive(Debug, Clone, Default)]
pub struct LayoutSettingParams {
pub layout_type: DatabaseLayout,
pub calendar: Option<CalendarLayoutSetting>,
}
impl From<LayoutSettingParams> for LayoutSettingPB {
impl From<LayoutSettingParams> for DatabaseLayoutSettingPB {
fn from(data: LayoutSettingParams) -> Self {
Self {
layout_type: data.layout_type.into(),
calendar: data.calendar.map(|calendar| calendar.into()),
}
}
@ -169,13 +174,17 @@ pub struct LayoutSettingChangesetPB {
#[pb(index = 1)]
pub view_id: String,
#[pb(index = 2, one_of)]
#[pb(index = 2)]
pub layout_type: DatabaseLayoutPB,
#[pb(index = 3, one_of)]
pub calendar: Option<CalendarLayoutSettingPB>,
}
#[derive(Debug)]
pub struct LayoutSettingChangeset {
pub view_id: String,
pub layout_type: DatabaseLayout,
pub calendar: Option<CalendarLayoutSetting>,
}
@ -189,6 +198,7 @@ impl TryInto<LayoutSettingChangeset> for LayoutSettingChangesetPB {
Ok(LayoutSettingChangeset {
view_id,
layout_type: self.layout_type.into(),
calendar: self.calendar.map(|calendar| calendar.into()),
})
}

View File

@ -2,7 +2,6 @@ use collab_database::database::gen_row_id;
use std::sync::Arc;
use collab_database::rows::RowId;
use collab_database::views::DatabaseLayout;
use lib_infra::util::timestamp;
use flowy_error::{FlowyError, FlowyResult};
@ -25,7 +24,7 @@ pub(crate) async fn get_database_data_handler(
) -> DataResult<DatabasePB, FlowyError> {
let view_id: DatabaseViewIdPB = data.into_inner();
let database_editor = manager.get_database_with_view_id(view_id.as_ref()).await?;
let data = database_editor.get_database_data(view_id.as_ref()).await;
let data = database_editor.get_database_data(view_id.as_ref()).await?;
data_result_ok(data)
}
@ -64,6 +63,12 @@ pub(crate) async fn update_database_setting_handler(
if let Some(delete_sort) = params.delete_sort {
editor.delete_sort(delete_sort).await?;
}
if let Some(layout_type) = params.layout_type {
editor
.update_view_layout(&params.view_id, layout_type)
.await?;
}
Ok(())
}
@ -626,24 +631,25 @@ pub(crate) async fn set_layout_setting_handler(
let params: LayoutSettingChangeset = data.into_inner().try_into()?;
let database_editor = manager.get_database_with_view_id(&params.view_id).await?;
let layout_params = LayoutSettingParams {
layout_type: params.layout_type,
calendar: params.calendar,
};
database_editor
.set_layout_setting(&params.view_id, DatabaseLayout::Calendar, layout_params)
.set_layout_setting(&params.view_id, layout_params)
.await;
Ok(())
}
pub(crate) async fn get_layout_setting_handler(
data: AFPluginData<DatabaseLayoutIdPB>,
data: AFPluginData<DatabaseLayoutMetaPB>,
manager: AFPluginState<Arc<DatabaseManager2>>,
) -> DataResult<LayoutSettingPB, FlowyError> {
let params: DatabaseLayoutId = data.into_inner().try_into()?;
) -> DataResult<DatabaseLayoutSettingPB, FlowyError> {
let params: DatabaseLayoutMeta = data.into_inner().try_into()?;
let database_editor = manager.get_database_with_view_id(&params.view_id).await?;
let layout_setting_pb = database_editor
.get_layout_setting(&params.view_id, params.layout)
.await
.map(LayoutSettingPB::from)
.map(DatabaseLayoutSettingPB::from)
.unwrap_or_default();
data_result_ok(layout_setting_pb)
}
@ -661,6 +667,19 @@ pub(crate) async fn get_calendar_events_handler(
data_result_ok(RepeatedCalendarEventPB { items: events })
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub(crate) async fn get_no_date_calendar_events_handler(
data: AFPluginData<CalendarEventRequestPB>,
manager: AFPluginState<Arc<DatabaseManager2>>,
) -> DataResult<RepeatedNoDateCalendarEventPB, FlowyError> {
let params: CalendarEventRequestParams = data.into_inner().try_into()?;
let database_editor = manager.get_database_with_view_id(&params.view_id).await?;
let _events = database_editor
.get_all_no_date_calendar_events(&params.view_id)
.await;
todo!()
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub(crate) async fn get_calendar_event_handler(
data: AFPluginData<RowIdPB>,
@ -699,3 +718,12 @@ pub(crate) async fn move_calendar_event_handler(
.await?;
Ok(())
}
#[tracing::instrument(level = "debug", skip_all, err)]
pub(crate) async fn create_database_view(
_data: AFPluginData<CreateDatabaseViewPayloadPB>,
_manager: AFPluginState<Arc<DatabaseManager2>>,
) -> FlowyResult<()> {
// let data: CreateDatabaseViewParams = data.into_inner().try_into()?;
Ok(())
}

View File

@ -60,12 +60,13 @@ pub fn init(database_manager: Arc<DatabaseManager2>) -> AFPlugin {
.event(DatabaseEvent::GetDatabases, get_databases_handler)
// Calendar
.event(DatabaseEvent::GetAllCalendarEvents, get_calendar_events_handler)
.event(DatabaseEvent::GetNoDateCalendarEvents, get_no_date_calendar_events_handler)
.event(DatabaseEvent::GetCalendarEvent, get_calendar_event_handler)
.event(DatabaseEvent::MoveCalendarEvent, move_calendar_event_handler)
// Layout setting
.event(DatabaseEvent::SetLayoutSetting, set_layout_setting_handler)
.event(DatabaseEvent::GetLayoutSetting, get_layout_setting_handler)
// import
.event(DatabaseEvent::CreateDatabaseView, create_database_view)
}
/// [DatabaseEvent] defines events that are used to interact with the Grid. You could check [this](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/backend/protobuf)
@ -265,15 +266,24 @@ pub enum DatabaseEvent {
#[event(input = "LayoutSettingChangesetPB")]
SetLayoutSetting = 121,
#[event(input = "DatabaseLayoutIdPB", output = "LayoutSettingPB")]
#[event(input = "DatabaseLayoutMetaPB", output = "DatabaseLayoutSettingPB")]
GetLayoutSetting = 122,
#[event(input = "CalendarEventRequestPB", output = "RepeatedCalendarEventPB")]
GetAllCalendarEvents = 123,
#[event(
input = "CalendarEventRequestPB",
output = "RepeatedNoDateCalendarEventPB"
)]
GetNoDateCalendarEvents = 124,
#[event(input = "RowIdPB", output = "CalendarEventPB")]
GetCalendarEvent = 124,
GetCalendarEvent = 125,
#[event(input = "MoveCalendarEventPB")]
MoveCalendarEvent = 125,
MoveCalendarEvent = 126,
#[event(input = "CreateDatabaseViewPayloadPB")]
CreateDatabaseView = 130,
}

View File

@ -171,8 +171,7 @@ impl DatabaseManager2 {
name: String,
layout: DatabaseLayoutPB,
database_id: String,
target_view_id: String,
duplicated_view_id: Option<String>,
database_view_id: String,
) -> FlowyResult<()> {
self.with_user_database(
Err(FlowyError::internal().context("Create database view failed")),
@ -180,15 +179,8 @@ impl DatabaseManager2 {
let database = user_database
.get_database(&database_id)
.ok_or_else(FlowyError::record_not_found)?;
match duplicated_view_id {
None => {
let params = CreateViewParams::new(database_id, target_view_id, name, layout.into());
database.create_linked_view(params)?;
},
Some(duplicated_view_id) => {
database.duplicate_linked_view(&duplicated_view_id);
},
}
let params = CreateViewParams::new(database_id, database_view_id, name, layout.into());
database.create_linked_view(params)?;
Ok(())
},
)?;
@ -228,6 +220,15 @@ impl DatabaseManager2 {
database.export_csv(style).await
}
pub async fn update_database_layout(
&self,
view_id: &str,
layout: DatabaseLayoutPB,
) -> FlowyResult<()> {
let database = self.get_database_with_view_id(view_id).await?;
database.update_view_layout(view_id, layout.into()).await
}
fn with_user_database<F, Output>(&self, default_value: Output, f: F) -> Output
where
F: FnOnce(&InnerUserDatabase) -> Output,

View File

@ -35,6 +35,8 @@ pub enum DatabaseNotification {
DidUpdateLayoutSettings = 80,
// Trigger when the layout field of the database is changed
DidSetNewLayoutField = 81,
// Trigger when the layout of the database is changed
DidUpdateDatabaseLayout = 82,
}
impl std::default::Default for DatabaseNotification {

View File

@ -18,8 +18,9 @@ use crate::entities::{
CalendarEventPB, CellChangesetNotifyPB, CellPB, ChecklistCellDataPB, DatabaseFieldChangesetPB,
DatabasePB, DatabaseViewSettingPB, DeleteFilterParams, DeleteGroupParams, DeleteSortParams,
FieldChangesetParams, FieldIdPB, FieldPB, FieldType, GroupPB, IndexFieldPB, InsertedRowPB,
LayoutSettingParams, RepeatedFilterPB, RepeatedGroupPB, RepeatedSortPB, RowPB, RowsChangePB,
SelectOptionCellDataPB, SelectOptionPB, UpdateFilterParams, UpdateSortParams,
LayoutSettingParams, NoDateCalendarEventPB, RepeatedFilterPB, RepeatedGroupPB, RepeatedSortPB,
RowPB, RowsChangePB, SelectOptionCellDataPB, SelectOptionPB, UpdateFilterParams,
UpdateSortParams, UpdatedRowPB,
};
use crate::notification::{send_notification, DatabaseNotification};
use crate::services::cell::{
@ -38,6 +39,7 @@ use crate::services::filter::Filter;
use crate::services::group::{
default_group_setting, GroupSetting, GroupSettingChangeset, RowChangeset,
};
use crate::services::share::csv::{CSVExport, CSVFormat};
use crate::services::sort::Sort;
@ -76,6 +78,17 @@ impl DatabaseEditor {
pub async fn close(&self) {}
pub async fn update_view_layout(
&self,
view_id: &str,
layout_type: DatabaseLayout,
) -> FlowyResult<()> {
let view_editor = self.database_views.get_view_editor(view_id).await?;
view_editor.v_update_layout_type(layout_type).await?;
Ok(())
}
pub async fn subscribe_view_changed(
&self,
view_id: &str,
@ -442,7 +455,7 @@ impl DatabaseEditor {
let (field, cell) = {
let database = self.database.lock();
let field = database.fields.get_field(field_id);
let cell = database.get_cell(field_id, &row_id);
let cell = database.get_cell(field_id, &row_id).cell;
(field, cell)
};
@ -484,12 +497,7 @@ impl DatabaseEditor {
Err(FlowyError::internal().context(msg))
},
}?;
(
field,
database
.get_cell(field_id, &row_id)
.map(|row_cell| row_cell.cell),
)
(field, database.get_cell(field_id, &row_id).cell)
};
let new_cell =
apply_cell_changeset(cell_changeset, cell, &field, Some(self.cell_cache.clone()))?;
@ -529,6 +537,15 @@ impl DatabaseEditor {
let option_row = self.database.lock().get_row(&row_id);
if let Some(new_row) = option_row {
let updated_row = UpdatedRowPB {
row: RowPB::from(&new_row),
field_ids: vec![field_id.to_string()],
};
let changes = RowsChangePB::from_update(view_id.to_string(), updated_row);
send_notification(view_id, DatabaseNotification::DidUpdateViewRows)
.payload(changes)
.send();
for view in self.database_views.editors().await {
view.v_did_update_row(&old_row, &new_row, field_id).await;
}
@ -646,10 +663,10 @@ impl DatabaseEditor {
match field {
None => SelectOptionCellDataPB::default(),
Some(field) => {
let row_cell = self.database.lock().get_cell(field_id, &row_id);
let ids = match row_cell {
let cell = self.database.lock().get_cell(field_id, &row_id).cell;
let ids = match cell {
None => SelectOptionIds::new(),
Some(row_cell) => SelectOptionIds::from(&row_cell.cell),
Some(cell) => SelectOptionIds::from(&cell),
};
match select_type_option_from_field(&field) {
Ok(type_option) => type_option.get_selected_options(ids).into(),
@ -661,9 +678,9 @@ impl DatabaseEditor {
pub async fn get_checklist_option(&self, row_id: RowId, field_id: &str) -> ChecklistCellDataPB {
let row_cell = self.database.lock().get_cell(field_id, &row_id);
let cell_data = match row_cell {
let cell_data = match row_cell.cell {
None => ChecklistCellData::default(),
Some(row_cell) => ChecklistCellData::from(&row_cell.cell),
Some(cell) => ChecklistCellData::from(&cell),
};
ChecklistCellDataPB::from(cell_data)
}
@ -763,14 +780,9 @@ impl DatabaseEditor {
Ok(())
}
pub async fn set_layout_setting(
&self,
view_id: &str,
layout_ty: DatabaseLayout,
layout_setting: LayoutSettingParams,
) {
pub async fn set_layout_setting(&self, view_id: &str, layout_setting: LayoutSettingParams) {
if let Ok(view) = self.database_views.get_view_editor(view_id).await {
let _ = view.v_set_layout_settings(&layout_ty, layout_setting).await;
let _ = view.v_set_layout_settings(layout_setting).await;
}
}
@ -795,6 +807,15 @@ impl DatabaseEditor {
}
}
#[tracing::instrument(level = "trace", skip_all)]
pub async fn get_all_no_date_calendar_events(
&self,
view_id: &str,
) -> FlowyResult<Vec<NoDateCalendarEventPB>> {
let _database_view = self.database_views.get_view_editor(view_id).await?;
todo!()
}
#[tracing::instrument(level = "trace", skip_all)]
pub async fn get_calendar_event(&self, view_id: &str, row_id: RowId) -> Option<CalendarEventPB> {
let view = self.database_views.get_view_editor(view_id).await.ok()?;
@ -858,8 +879,13 @@ impl DatabaseEditor {
Ok(database_view_setting_pb_from_view(view))
}
pub async fn get_database_data(&self, view_id: &str) -> DatabasePB {
let rows = self.get_rows(view_id).await.unwrap_or_default();
pub async fn get_database_data(&self, view_id: &str) -> FlowyResult<DatabasePB> {
let database_view = self.database_views.get_view_editor(view_id).await?;
let view = database_view
.get_view()
.await
.ok_or(FlowyError::record_not_found())?;
let rows = database_view.v_get_rows().await;
let (database_id, fields) = {
let database = self.database.lock();
let database_id = database.get_database_id();
@ -876,11 +902,12 @@ impl DatabaseEditor {
.into_iter()
.map(|row| RowPB::from(row.as_ref()))
.collect::<Vec<RowPB>>();
DatabasePB {
Ok(DatabasePB {
id: database_id,
fields,
rows,
}
layout_type: view.layout.into(),
})
}
pub async fn export_csv(&self, style: CSVFormat) -> FlowyResult<String> {
@ -946,7 +973,7 @@ struct DatabaseViewDataImpl {
}
impl DatabaseViewData for DatabaseViewDataImpl {
fn get_view_setting(&self, view_id: &str) -> Fut<Option<DatabaseView>> {
fn get_view(&self, view_id: &str) -> Fut<Option<DatabaseView>> {
let view = self.database.lock().get_view(view_id);
to_fut(async move { view })
}
@ -966,6 +993,26 @@ impl DatabaseViewData for DatabaseViewDataImpl {
to_fut(async move { field })
}
fn create_field(
&self,
view_id: &str,
name: &str,
field_type: FieldType,
type_option_data: TypeOptionData,
) -> Fut<Field> {
let (_, field) = self.database.lock().create_default_field(
view_id,
name.to_string(),
field_type.clone().into(),
|field| {
field
.type_options
.insert(field_type.to_string(), type_option_data);
},
);
to_fut(async move { field })
}
fn get_primary_field(&self) -> Fut<Option<Arc<Field>>> {
let field = self
.database
@ -1002,18 +1049,13 @@ impl DatabaseViewData for DatabaseViewDataImpl {
to_fut(async move { cells.into_iter().map(Arc::new).collect() })
}
fn get_cell_in_row(&self, field_id: &str, row_id: &RowId) -> Fut<Option<Arc<RowCell>>> {
fn get_cell_in_row(&self, field_id: &str, row_id: &RowId) -> Fut<Arc<RowCell>> {
let cell = self.database.lock().get_cell(field_id, row_id);
to_fut(async move { cell.map(Arc::new) })
to_fut(async move { Arc::new(cell) })
}
fn get_layout_for_view(&self, view_id: &str) -> DatabaseLayout {
self
.database
.lock()
.views
.get_view_layout(view_id)
.unwrap_or_default()
self.database.lock().views.get_database_view_layout(view_id)
}
fn get_group_setting(&self, view_id: &str) -> Vec<GroupSetting> {
@ -1077,11 +1119,7 @@ impl DatabaseViewData for DatabaseViewDataImpl {
}
fn get_layout_setting(&self, view_id: &str, layout_ty: &DatabaseLayout) -> Option<LayoutSetting> {
self
.database
.lock()
.views
.get_layout_setting(view_id, layout_ty)
self.database.lock().get_layout_setting(view_id, layout_ty)
}
fn insert_layout_setting(
@ -1096,6 +1134,17 @@ impl DatabaseViewData for DatabaseViewDataImpl {
.insert_layout_setting(view_id, layout_ty, layout_setting);
}
fn get_layout_type(&self, view_id: &str) -> DatabaseLayout {
self.database.lock().views.get_database_view_layout(view_id)
}
fn update_layout_type(&self, view_id: &str, layout_type: &DatabaseLayout) {
self
.database
.lock()
.update_layout_type(view_id, layout_type);
}
fn get_task_scheduler(&self) -> Arc<RwLock<TaskDispatcher>> {
self.task_scheduler.clone()
}

View File

@ -1,5 +1,5 @@
use collab_database::rows::RowId;
use collab_database::views::RowOrder;
use collab_database::views::{DatabaseLayout, RowOrder};
#[derive(Debug, Clone)]
pub enum DatabaseRowEvent {
@ -25,3 +25,10 @@ pub struct UpdatedRow {
// represents as the cells that were updated in this row.
pub field_ids: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct CreateDatabaseViewParams {
pub name: String,
pub view_id: String,
pub layout_type: DatabaseLayout,
}

View File

@ -1,6 +1,6 @@
use crate::entities::{
CalendarLayoutSettingPB, DatabaseLayoutPB, DatabaseViewSettingPB, FilterPB, GroupSettingPB,
LayoutSettingPB, SortPB,
CalendarLayoutSettingPB, DatabaseLayoutPB, DatabaseLayoutSettingPB, DatabaseViewSettingPB,
FilterPB, GroupSettingPB, SortPB,
};
use crate::services::filter::Filter;
use crate::services::group::GroupSetting;
@ -9,17 +9,18 @@ use crate::services::sort::Sort;
use collab_database::views::DatabaseView;
pub(crate) fn database_view_setting_pb_from_view(view: DatabaseView) -> DatabaseViewSettingPB {
let layout_type: DatabaseLayoutPB = view.layout.clone().into();
let layout_setting = if let Some(layout_setting) = view.layout_settings.get(&view.layout) {
let calendar_setting =
CalendarLayoutSettingPB::from(CalendarLayoutSetting::from(layout_setting.clone()));
LayoutSettingPB {
DatabaseLayoutSettingPB {
layout_type: layout_type.clone(),
calendar: Some(calendar_setting),
}
} else {
LayoutSettingPB::default()
DatabaseLayoutSettingPB::default()
};
let current_layout: DatabaseLayoutPB = view.layout.into();
let filters = view
.filters
.into_iter()
@ -47,7 +48,7 @@ pub(crate) fn database_view_setting_pb_from_view(view: DatabaseView) -> Database
.collect::<Vec<SortPB>>();
DatabaseViewSettingPB {
current_layout,
layout_type,
filters: filters.into(),
group_settings: group_settings.into(),
sorts: sorts.into(),

View File

@ -3,7 +3,7 @@ use std::collections::HashMap;
use std::sync::Arc;
use collab_database::database::{gen_database_filter_id, gen_database_sort_id};
use collab_database::fields::Field;
use collab_database::fields::{Field, TypeOptionData};
use collab_database::rows::{Cells, Row, RowCell, RowId};
use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting, RowOrder};
use tokio::sync::{broadcast, RwLock};
@ -13,9 +13,9 @@ use flowy_task::TaskDispatcher;
use lib_infra::future::Fut;
use crate::entities::{
CalendarEventPB, DeleteFilterParams, DeleteGroupParams, DeleteSortParams, FieldType,
GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedRowPB, LayoutSettingPB,
LayoutSettingParams, RowPB, RowsChangePB, SortChangesetNotificationPB, SortPB,
CalendarEventPB, DatabaseLayoutMetaPB, DatabaseLayoutSettingPB, DeleteFilterParams,
DeleteGroupParams, DeleteSortParams, FieldType, GroupChangesPB, GroupPB, GroupRowsNotificationPB,
InsertedRowPB, LayoutSettingParams, RowPB, RowsChangePB, SortChangesetNotificationPB, SortPB,
UpdateFilterParams, UpdateSortParams,
};
use crate::notification::{send_notification, DatabaseNotification};
@ -31,7 +31,7 @@ use crate::services::database_view::{
notify_did_update_setting, notify_did_update_sort, DatabaseViewChangedNotifier,
DatabaseViewChangedReceiverRunner,
};
use crate::services::field::TypeOptionCellDataHandler;
use crate::services::field::{DateTypeOption, TypeOptionCellDataHandler};
use crate::services::filter::{
Filter, FilterChangeset, FilterController, FilterType, UpdatedFilterType,
};
@ -42,13 +42,21 @@ use crate::services::setting::CalendarLayoutSetting;
use crate::services::sort::{DeletedSortType, Sort, SortChangeset, SortController, SortType};
pub trait DatabaseViewData: Send + Sync + 'static {
fn get_view_setting(&self, view_id: &str) -> Fut<Option<DatabaseView>>;
fn get_view(&self, view_id: &str) -> Fut<Option<DatabaseView>>;
/// If the field_ids is None, then it will return all the field revisions
fn get_fields(&self, view_id: &str, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<Field>>>;
/// Returns the field with the field_id
fn get_field(&self, field_id: &str) -> Fut<Option<Arc<Field>>>;
fn create_field(
&self,
view_id: &str,
name: &str,
field_type: FieldType,
type_option_data: TypeOptionData,
) -> Fut<Field>;
fn get_primary_field(&self) -> Fut<Option<Arc<Field>>>;
/// Returns the index of the row with row_id
@ -62,7 +70,7 @@ pub trait DatabaseViewData: Send + Sync + 'static {
fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Fut<Vec<Arc<RowCell>>>;
fn get_cell_in_row(&self, field_id: &str, row_id: &RowId) -> Fut<Option<Arc<RowCell>>>;
fn get_cell_in_row(&self, field_id: &str, row_id: &RowId) -> Fut<Arc<RowCell>>;
fn get_layout_for_view(&self, view_id: &str) -> DatabaseLayout;
@ -99,6 +107,12 @@ pub trait DatabaseViewData: Send + Sync + 'static {
layout_setting: LayoutSetting,
);
/// Return the database layout type for the view with given view_id
/// The default layout type is [DatabaseLayout::Grid]
fn get_layout_type(&self, view_id: &str) -> DatabaseLayout;
fn update_layout_type(&self, view_id: &str, layout_type: &DatabaseLayout);
/// Returns a `TaskDispatcher` used to poll a `Task`
fn get_task_scheduler(&self) -> Arc<RwLock<TaskDispatcher>>;
@ -167,6 +181,10 @@ impl DatabaseViewEditor {
self.filter_controller.close().await;
}
pub async fn get_view(&self) -> Option<DatabaseView> {
self.delegate.get_view(&self.view_id).await
}
pub async fn v_will_create_row(&self, cells: &mut Cells, group_id: &Option<String>) {
if group_id.is_none() {
return;
@ -398,7 +416,7 @@ impl DatabaseViewEditor {
if !is_grouping_field {
self.v_update_grouping_field(field_id).await?;
if let Some(view) = self.delegate.get_view_setting(&self.view_id).await {
if let Some(view) = self.delegate.get_view(&self.view_id).await {
let setting = database_view_setting_pb_from_view(view);
notify_did_update_setting(&self.view_id, setting).await;
}
@ -571,16 +589,11 @@ impl DatabaseViewEditor {
},
}
tracing::debug!("{:?}", layout_setting);
layout_setting
}
/// Update the calendar settings and send the notification to refresh the UI
pub async fn v_set_layout_settings(
&self,
_layout_ty: &DatabaseLayout,
params: LayoutSettingParams,
) -> FlowyResult<()> {
pub async fn v_set_layout_settings(&self, params: LayoutSettingParams) -> FlowyResult<()> {
// Maybe it needs no send notification to refresh the UI
if let Some(new_calendar_setting) = params.calendar {
if let Some(field) = self
@ -593,16 +606,19 @@ impl DatabaseViewEditor {
return Err(FlowyError::unexpect_calendar_field_type());
}
let layout_ty = DatabaseLayout::Calendar;
let old_calender_setting = self.v_get_layout_settings(&layout_ty).await.calendar;
let old_calender_setting = self
.v_get_layout_settings(&params.layout_type)
.await
.calendar;
self.delegate.insert_layout_setting(
&self.view_id,
&layout_ty,
&params.layout_type,
new_calendar_setting.clone().into(),
);
let new_field_id = new_calendar_setting.field_id.clone();
let layout_setting_pb: LayoutSettingPB = LayoutSettingParams {
let layout_setting_pb: DatabaseLayoutSettingPB = LayoutSettingParams {
layout_type: params.layout_type,
calendar: Some(new_calendar_setting),
}
.into();
@ -620,8 +636,6 @@ impl DatabaseViewEditor {
.payload(layout_setting_pb)
.send();
}
} else {
tracing::warn!("Calendar setting should not be empty")
}
}
}
@ -788,6 +802,66 @@ impl DatabaseViewEditor {
Some(events)
}
#[tracing::instrument(level = "trace", skip_all)]
pub async fn v_update_layout_type(&self, layout_type: DatabaseLayout) -> FlowyResult<()> {
self
.delegate
.update_layout_type(&self.view_id, &layout_type);
// Update the layout type in the database might add a new field to the database. If the new
// layout type is a calendar and there is not date field in the database, it will add a new
// date field to the database and create the corresponding layout setting.
//
let fields = self.delegate.get_fields(&self.view_id, None).await;
let date_field_id = match fields
.into_iter()
.find(|field| FieldType::from(field.field_type) == FieldType::DateTime)
{
None => {
tracing::trace!("Create a new date field after layout type change");
let default_date_type_option = DateTypeOption::default();
let field = self
.delegate
.create_field(
&self.view_id,
"Date",
FieldType::DateTime,
default_date_type_option.into(),
)
.await;
field.id
},
Some(date_field) => date_field.id.clone(),
};
let layout_setting = self.v_get_layout_settings(&layout_type).await;
match layout_type {
DatabaseLayout::Grid => {},
DatabaseLayout::Board => {},
DatabaseLayout::Calendar => {
if layout_setting.calendar.is_none() {
let layout_setting = CalendarLayoutSetting::new(date_field_id.clone());
self
.v_set_layout_settings(LayoutSettingParams {
layout_type,
calendar: Some(layout_setting),
})
.await?;
}
},
}
let payload = DatabaseLayoutMetaPB {
view_id: self.view_id.clone(),
layout: layout_type.into(),
};
send_notification(&self.view_id, DatabaseNotification::DidUpdateDatabaseLayout)
.payload(payload)
.send();
Ok(())
}
pub async fn handle_row_event(&self, event: Cow<'_, DatabaseRowEvent>) {
let changeset = match event.into_owned() {
DatabaseRowEvent::InsertRow(row) => {

View File

@ -52,10 +52,8 @@ pub async fn new_group_controller(
let layout = delegate.get_layout_for_view(&view_id);
// If the view is a board and the grouping field is empty, we need to find a new grouping field
if layout.is_board() {
if grouping_field.is_none() {
grouping_field = find_new_grouping_field(&fields, &layout);
}
if layout.is_board() && grouping_field.is_none() {
grouping_field = find_new_grouping_field(&fields, &layout);
}
if let Some(grouping_field) = grouping_field {
@ -104,21 +102,20 @@ pub(crate) async fn get_cell_for_row(
row_id: &RowId,
) -> Option<RowSingleCellData> {
let field = delegate.get_field(field_id).await?;
let cell = delegate.get_cell_in_row(field_id, row_id).await?;
let row_cell = delegate.get_cell_in_row(field_id, row_id).await;
let field_type = FieldType::from(field.field_type);
let handler = delegate.get_type_option_cell_handler(&field, &field_type)?;
if let Some(handler) = delegate.get_type_option_cell_handler(&field, &field_type) {
return match handler.get_cell_data(&cell, &field_type, &field) {
Ok(cell_data) => Some(RowSingleCellData {
row_id: cell.row_id.clone(),
field_id: field.id.clone(),
field_type: field_type.clone(),
cell_data,
}),
Err(_) => None,
};
}
None
let cell_data = match &row_cell.cell {
None => None,
Some(cell) => handler.get_cell_data(&cell, &field_type, &field).ok(),
};
Some(RowSingleCellData {
row_id: row_cell.row_id.clone(),
field_id: field.id.clone(),
field_type: field_type.clone(),
cell_data,
})
}
// Returns the list of cells corresponding to the given field.
@ -133,17 +130,18 @@ pub(crate) async fn get_cells_for_field(
let cells = delegate.get_cells_for_field(view_id, field_id).await;
return cells
.iter()
.flat_map(
|cell| match handler.get_cell_data(cell, &field_type, &field) {
Ok(cell_data) => Some(RowSingleCellData {
row_id: cell.row_id.clone(),
field_id: field.id.clone(),
field_type: field_type.clone(),
cell_data,
}),
Err(_) => None,
},
)
.map(|row_cell| {
let cell_data = match &row_cell.cell {
None => None,
Some(cell) => handler.get_cell_data(&cell, &field_type, &field).ok(),
};
RowSingleCellData {
row_id: row_cell.row_id.clone(),
field_id: field.id.clone(),
field_type: field_type.clone(),
cell_data,
}
})
.collect();
}
}

View File

@ -19,7 +19,7 @@ use std::str::FromStr;
/// The [DateTypeOption] is used by [FieldType::Date], [FieldType::LastEditedTime], and [FieldType::CreatedTime].
/// So, storing the field type is necessary to distinguish the field type.
/// Most of the cases, each [FieldType] has its own [TypeOption] implementation.
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DateTypeOption {
pub date_format: DateFormat,
pub time_format: TimeFormat,
@ -27,6 +27,17 @@ pub struct DateTypeOption {
pub field_type: FieldType,
}
impl Default for DateTypeOption {
fn default() -> Self {
Self {
date_format: Default::default(),
time_format: Default::default(),
timezone_id: Default::default(),
field_type: FieldType::DateTime,
}
}
}
impl TypeOption for DateTypeOption {
type CellData = DateCellData;
type CellChangeset = DateCellChangeset;

View File

@ -498,14 +498,14 @@ pub struct RowSingleCellData {
pub row_id: RowId,
pub field_id: String,
pub field_type: FieldType,
pub cell_data: BoxCellData,
pub cell_data: Option<BoxCellData>,
}
macro_rules! into_cell_data {
($func_name:ident,$return_ty:ty) => {
#[allow(dead_code)]
pub fn $func_name(self) -> Option<$return_ty> {
self.cell_data.unbox_or_none()
self.cell_data?.unbox_or_none()
}
};
}

View File

@ -254,6 +254,7 @@ where
continue;
}
}
match self.context.get_mut_no_status_group() {
None => {},
Some(no_status_group) => no_status_group.add_row((*row).clone()),
@ -349,12 +350,12 @@ where
deleted_group: None,
row_changesets: vec![],
};
let cell_rev = match context.row.cells.get(&self.grouping_field_id) {
Some(cell_rev) => Some(cell_rev.clone()),
let cell = match context.row.cells.get(&self.grouping_field_id) {
Some(cell) => Some(cell.clone()),
None => self.placeholder_cell(),
};
if let Some(cell) = cell_rev {
if let Some(cell) = cell {
let cell_bytes = get_cell_protobuf(&cell, context.field, None);
let cell_data = cell_bytes.parser::<P>()?;
result.deleted_group = self.delete_group_when_move_row(context.row, &cell_data);

View File

@ -1,4 +1,4 @@
use crate::entities::{GroupRowsNotificationPB, SelectOptionCellDataPB};
use crate::entities::{FieldType, GroupRowsNotificationPB, SelectOptionCellDataPB};
use crate::services::cell::insert_select_option_cell;
use crate::services::field::{MultiSelectTypeOption, SelectOptionCellDataParser};
use crate::services::group::action::GroupCustomize;
@ -10,7 +10,7 @@ use crate::services::group::{
move_group_row, remove_select_option_row, GeneratedGroups, GroupContext,
};
use collab_database::fields::Field;
use collab_database::rows::{Cells, Row};
use collab_database::rows::{new_cell_builder, Cell, Cells, Row};
use std::sync::Arc;
use serde::{Deserialize, Serialize};
@ -39,6 +39,14 @@ impl GroupCustomize for MultiSelectGroupController {
.any(|option| option.id == content)
}
fn placeholder_cell(&self) -> Option<Cell> {
Some(
new_cell_builder(FieldType::MultiSelect)
.insert_str_value("data", "")
.build(),
)
}
fn add_or_remove_row_when_cell_changed(
&mut self,
row: &Row,

View File

@ -1,9 +1,9 @@
use crate::entities::{GroupRowsNotificationPB, SelectOptionCellDataPB};
use crate::entities::{FieldType, GroupRowsNotificationPB, SelectOptionCellDataPB};
use crate::services::cell::insert_select_option_cell;
use crate::services::field::{SelectOptionCellDataParser, SingleSelectTypeOption};
use crate::services::group::action::GroupCustomize;
use collab_database::fields::Field;
use collab_database::rows::{Cells, Row};
use collab_database::rows::{new_cell_builder, Cell, Cells, Row};
use std::sync::Arc;
use crate::services::group::controller::{
@ -39,6 +39,14 @@ impl GroupCustomize for SingleSelectGroupController {
.any(|option| option.id == content)
}
fn placeholder_cell(&self) -> Option<Cell> {
Some(
new_cell_builder(FieldType::SingleSelect)
.insert_str_value("data", "")
.build(),
)
}
fn add_or_remove_row_when_cell_changed(
&mut self,
row: &Row,

View File

@ -3,10 +3,10 @@ mod configuration;
mod controller;
mod controller_impls;
mod entities;
mod group_util;
mod group_builder;
pub(crate) use configuration::*;
pub(crate) use controller::*;
pub(crate) use controller_impls::*;
pub(crate) use entities::*;
pub(crate) use group_util::*;
pub(crate) use group_builder::*;

View File

@ -74,8 +74,8 @@ async fn text_cell_data_test() {
.get_cells_for_field(&test.view_id, &text_field.id)
.await;
for (i, cell) in cells.into_iter().enumerate() {
let text = StrCellData::from(cell.as_ref());
for (i, row_cell) in cells.into_iter().enumerate() {
let text = StrCellData::from(row_cell.cell.as_ref().unwrap());
match i {
0 => assert_eq!(text.as_str(), "A"),
1 => assert_eq!(text.as_str(), ""),
@ -97,10 +97,12 @@ async fn url_cell_data_test() {
.get_cells_for_field(&test.view_id, &url_field.id)
.await;
for (i, cell) in cells.into_iter().enumerate() {
let cell = URLCellData::from(cell.as_ref());
if i == 0 {
assert_eq!(cell.url.as_str(), "https://www.appflowy.io/");
for (i, row_cell) in cells.into_iter().enumerate() {
if let Some(cell) = row_cell.cell.as_ref() {
let cell = URLCellData::from(cell);
if i == 0 {
assert_eq!(cell.url.as_str(), "https://www.appflowy.io/");
}
}
}
}
@ -135,8 +137,10 @@ async fn update_updated_at_field_on_other_cell_update() {
.get_cells_for_field(&test.view_id, &updated_at_field.id)
.await;
assert!(!cells.is_empty());
for (i, cell) in cells.into_iter().enumerate() {
let timestamp = DateCellData::from(cell.as_ref()).timestamp.unwrap();
for (i, row_cell) in cells.into_iter().enumerate() {
let timestamp = DateCellData::from(row_cell.cell.as_ref().unwrap())
.timestamp
.unwrap();
println!(
"{}, bf: {}, af: {}",
timestamp, before_update_timestamp, after_update_timestamp

View File

@ -6,7 +6,7 @@ use collab_database::fields::Field;
use collab_database::rows::{CreateRowParams, Row, RowId};
use strum::EnumCount;
use flowy_database2::entities::{DatabaseLayoutPB, FieldType, FilterPB, RowPB, SelectOptionPB};
use flowy_database2::entities::{FieldType, FilterPB, RowPB, SelectOptionPB};
use flowy_database2::services::cell::{CellBuilder, ToCellChangeset};
use flowy_database2::services::database::DatabaseEditor;
use flowy_database2::services::field::checklist_type_option::{
@ -21,7 +21,9 @@ use flowy_error::FlowyResult;
use flowy_test::folder_event::ViewTest;
use flowy_test::FlowyCoreTest;
use crate::database::mock_data::{make_test_board, make_test_calendar, make_test_grid};
use crate::database::mock_data::{
make_no_date_test_grid, make_test_board, make_test_calendar, make_test_grid,
};
pub struct DatabaseEditorTest {
pub sdk: FlowyCoreTest,
@ -36,35 +38,42 @@ pub struct DatabaseEditorTest {
impl DatabaseEditorTest {
pub async fn new_grid() -> Self {
Self::new(DatabaseLayoutPB::Grid).await
let sdk = FlowyCoreTest::new();
let _ = sdk.init_user().await;
let params = make_test_grid();
let view_test = ViewTest::new_grid_view(&sdk, params.to_json_bytes().unwrap()).await;
Self::new(sdk, view_test).await
}
pub async fn new_no_date_grid() -> Self {
let sdk = FlowyCoreTest::new();
let _ = sdk.init_user().await;
let params = make_no_date_test_grid();
let view_test = ViewTest::new_grid_view(&sdk, params.to_json_bytes().unwrap()).await;
Self::new(sdk, view_test).await
}
pub async fn new_board() -> Self {
Self::new(DatabaseLayoutPB::Board).await
let sdk = FlowyCoreTest::new();
let _ = sdk.init_user().await;
let params = make_test_board();
let view_test = ViewTest::new_grid_view(&sdk, params.to_json_bytes().unwrap()).await;
Self::new(sdk, view_test).await
}
pub async fn new_calendar() -> Self {
Self::new(DatabaseLayoutPB::Calendar).await
}
pub async fn new(layout: DatabaseLayoutPB) -> Self {
let sdk = FlowyCoreTest::new();
let _ = sdk.init_user().await;
let test = match layout {
DatabaseLayoutPB::Grid => {
let params = make_test_grid();
ViewTest::new_grid_view(&sdk, params.to_json_bytes().unwrap()).await
},
DatabaseLayoutPB::Board => {
let data = make_test_board();
ViewTest::new_board_view(&sdk, data.to_json_bytes().unwrap()).await
},
DatabaseLayoutPB::Calendar => {
let data = make_test_calendar();
ViewTest::new_calendar_view(&sdk, data.to_json_bytes().unwrap()).await
},
};
let params = make_test_calendar();
let view_test = ViewTest::new_grid_view(&sdk, params.to_json_bytes().unwrap()).await;
Self::new(sdk, view_test).await
}
pub async fn new(sdk: FlowyCoreTest, test: ViewTest) -> Self {
let editor = sdk
.database_manager
.get_database_with_view_id(&test.child_view.id)

View File

@ -265,7 +265,7 @@ impl DatabaseFilterTest {
assert_eq!(expected_setting, setting);
}
FilterScript::AssertNumberOfVisibleRows { expected } => {
let grid = self.editor.get_database_data(&self.view_id).await;
let grid = self.editor.get_database_data(&self.view_id).await.unwrap();
assert_eq!(grid.rows.len(), expected);
}
FilterScript::Wait { millisecond } => {

View File

@ -8,7 +8,9 @@ use crate::database::database_editor::DatabaseEditorTest;
pub enum LayoutScript {
AssertCalendarLayoutSetting { expected: CalendarLayoutSetting },
GetCalendarEvents,
AssertDefaultAllCalendarEvents,
AssertAllCalendarEventsCount { expected: usize },
UpdateDatabaseLayout { layout: DatabaseLayout },
}
pub struct DatabaseLayoutTest {
@ -16,6 +18,11 @@ pub struct DatabaseLayoutTest {
}
impl DatabaseLayoutTest {
pub async fn new_no_date_grid() -> Self {
let database_test = DatabaseEditorTest::new_no_date_grid().await;
Self { database_test }
}
pub async fn new_calendar() -> Self {
let database_test = DatabaseEditorTest::new_calendar().await;
Self { database_test }
@ -33,6 +40,22 @@ impl DatabaseLayoutTest {
pub async fn run_script(&mut self, script: LayoutScript) {
match script {
LayoutScript::UpdateDatabaseLayout { layout } => {
self
.database_test
.editor
.update_view_layout(&self.database_test.view_id, layout)
.await
.unwrap();
},
LayoutScript::AssertAllCalendarEventsCount { expected } => {
let events = self
.database_test
.editor
.get_all_calendar_events(&self.database_test.view_id)
.await;
assert_eq!(events.len(), expected);
},
LayoutScript::AssertCalendarLayoutSetting { expected } => {
let view_id = self.database_test.view_id.clone();
let layout_ty = DatabaseLayout::Calendar;
@ -53,7 +76,7 @@ impl DatabaseLayoutTest {
);
assert_eq!(calendar_setting.show_weekends, expected.show_weekends);
},
LayoutScript::GetCalendarEvents => {
LayoutScript::AssertDefaultAllCalendarEvents => {
let events = self
.database_test
.editor

View File

@ -1,3 +1,4 @@
use collab_database::views::DatabaseLayout;
use flowy_database2::services::setting::CalendarLayoutSetting;
use crate::database::layout_test::script::DatabaseLayoutTest;
@ -17,6 +18,18 @@ async fn calendar_initial_layout_setting_test() {
#[tokio::test]
async fn calendar_get_events_test() {
let mut test = DatabaseLayoutTest::new_calendar().await;
let scripts = vec![GetCalendarEvents];
let scripts = vec![AssertDefaultAllCalendarEvents];
test.run_scripts(scripts).await;
}
#[tokio::test]
async fn grid_to_calendar_layout_test() {
let mut test = DatabaseLayoutTest::new_no_date_grid().await;
let scripts = vec![
UpdateDatabaseLayout {
layout: DatabaseLayout::Calendar,
},
AssertAllCalendarEventsCount { expected: 3 },
];
test.run_scripts(scripts).await;
}

View File

@ -240,3 +240,80 @@ pub fn make_test_grid() -> DatabaseData {
DatabaseData { view, fields, rows }
}
pub fn make_no_date_test_grid() -> DatabaseData {
let mut fields = vec![];
let mut rows = vec![];
// Iterate through the FieldType to create the corresponding Field.
for field_type in FieldType::iter() {
match field_type {
FieldType::RichText => {
let text_field = FieldBuilder::from_field_type(field_type.clone())
.name("Name")
.visibility(true)
.primary(true)
.build();
fields.push(text_field);
},
FieldType::Number => {
// Number
let mut type_option = NumberTypeOption::default();
type_option.set_format(NumberFormat::USD);
let number_field = FieldBuilder::new(field_type.clone(), type_option)
.name("Price")
.visibility(true)
.build();
fields.push(number_field);
},
_ => {},
}
}
for i in 0..3 {
let mut row_builder = TestRowBuilder::new(i.into(), &fields);
match i {
0 => {
for field_type in FieldType::iter() {
match field_type {
FieldType::RichText => row_builder.insert_text_cell("A"),
FieldType::Number => row_builder.insert_number_cell("1"),
_ => "".to_owned(),
};
}
},
1 => {
for field_type in FieldType::iter() {
match field_type {
FieldType::RichText => row_builder.insert_text_cell(""),
FieldType::Number => row_builder.insert_number_cell("2"),
_ => "".to_owned(),
};
}
},
2 => {
for field_type in FieldType::iter() {
match field_type {
FieldType::RichText => row_builder.insert_text_cell("C"),
FieldType::Number => row_builder.insert_number_cell("3"),
_ => "".to_owned(),
};
}
},
_ => {},
}
let row = row_builder.build();
rows.push(row);
}
let view = DatabaseView {
id: gen_database_view_id(),
database_id: gen_database_id(),
name: "".to_string(),
layout: DatabaseLayout::Grid,
..Default::default()
};
DatabaseData { view, fields, rows }
}