mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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:
@ -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>,
|
||||
}
|
||||
|
@ -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,
|
||||
})
|
||||
|
@ -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>,
|
||||
}
|
||||
|
@ -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()),
|
||||
})
|
||||
}
|
||||
|
@ -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(¶ms.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(¶ms.view_id).await?;
|
||||
let layout_params = LayoutSettingParams {
|
||||
layout_type: params.layout_type,
|
||||
calendar: params.calendar,
|
||||
};
|
||||
database_editor
|
||||
.set_layout_setting(¶ms.view_id, DatabaseLayout::Calendar, layout_params)
|
||||
.set_layout_setting(¶ms.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(¶ms.view_id).await?;
|
||||
let layout_setting_pb = database_editor
|
||||
.get_layout_setting(¶ms.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(¶ms.view_id).await?;
|
||||
let _events = database_editor
|
||||
.get_all_no_date_calendar_events(¶ms.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(())
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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(¶ms.layout_type)
|
||||
.await
|
||||
.calendar;
|
||||
|
||||
self.delegate.insert_layout_setting(
|
||||
&self.view_id,
|
||||
&layout_ty,
|
||||
¶ms.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) => {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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::*;
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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 } => {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 }
|
||||
}
|
||||
|
Reference in New Issue
Block a user