mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: create database view on same database (#2829)
* feat: create database view on same database * feat: switch tag between views * fix: calendar tool bar * fix: set layout setting * chore: update collab rev * fix: board layout issue * test: add integration tests * test: add calendar start from day test
This commit is contained in:
@ -110,6 +110,9 @@ pub struct CalendarEventPB {
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub timestamp: i64,
|
||||
|
||||
#[pb(index = 5)]
|
||||
pub is_scheduled: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||
|
@ -23,6 +23,9 @@ pub struct DatabasePB {
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub layout_type: DatabaseLayoutPB,
|
||||
|
||||
#[pb(index = 5)]
|
||||
pub is_linked: bool,
|
||||
}
|
||||
|
||||
#[derive(ProtoBuf, Default)]
|
||||
|
@ -7,7 +7,7 @@ use appflowy_integrate::{CollabPersistenceConfig, RocksCollabDB};
|
||||
use collab::core::collab::MutexCollab;
|
||||
use collab_database::database::DatabaseData;
|
||||
use collab_database::user::{DatabaseCollabBuilder, UserDatabase as InnerUserDatabase};
|
||||
use collab_database::views::{CreateDatabaseParams, CreateViewParams};
|
||||
use collab_database::views::{CreateDatabaseParams, CreateViewParams, DatabaseLayout};
|
||||
use parking_lot::Mutex;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
@ -16,6 +16,7 @@ use flowy_task::TaskDispatcher;
|
||||
|
||||
use crate::entities::{DatabaseDescriptionPB, DatabaseLayoutPB, RepeatedDatabaseDescriptionPB};
|
||||
use crate::services::database::{DatabaseEditor, MutexDatabase};
|
||||
use crate::services::database_view::DatabaseLayoutDepsResolver;
|
||||
use crate::services::share::csv::{CSVFormat, CSVImporter, ImportResult};
|
||||
|
||||
pub trait DatabaseUser2: Send + Sync {
|
||||
@ -179,18 +180,28 @@ impl DatabaseManager2 {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A linked view is a view that is linked to existing database.
|
||||
#[tracing::instrument(level = "trace", skip(self), err)]
|
||||
pub async fn create_linked_view(
|
||||
&self,
|
||||
name: String,
|
||||
layout: DatabaseLayoutPB,
|
||||
layout: DatabaseLayout,
|
||||
database_id: String,
|
||||
database_view_id: String,
|
||||
) -> FlowyResult<()> {
|
||||
self.with_user_database(
|
||||
Err(FlowyError::internal().context("Create database view failed")),
|
||||
|user_database| {
|
||||
let params = CreateViewParams::new(database_id, database_view_id, name, layout.into());
|
||||
let mut params = CreateViewParams::new(database_id.clone(), database_view_id, name, layout);
|
||||
if let Some(database) = user_database.get_database(&database_id) {
|
||||
if let Some((field, layout_setting)) = DatabaseLayoutDepsResolver::new(database, layout)
|
||||
.resolve_deps_when_create_database_linked_view()
|
||||
{
|
||||
params = params
|
||||
.with_deps_fields(vec![field])
|
||||
.with_layout_setting(layout_setting);
|
||||
}
|
||||
};
|
||||
user_database.create_database_linked_view(params)?;
|
||||
Ok(())
|
||||
},
|
||||
|
@ -206,7 +206,7 @@ impl DatabaseEditor {
|
||||
.map(|field| field.id)
|
||||
.collect()
|
||||
});
|
||||
database.get_fields(view_id, Some(field_ids))
|
||||
database.get_fields_in_view(view_id, Some(field_ids))
|
||||
}
|
||||
|
||||
pub async fn update_field(&self, params: FieldChangesetParams) -> FlowyResult<()> {
|
||||
@ -442,7 +442,7 @@ impl DatabaseEditor {
|
||||
self
|
||||
.database
|
||||
.lock()
|
||||
.create_default_field(view_id, name, field_type.into(), |field| {
|
||||
.create_field_with_mut(view_id, name, field_type.into(), |field| {
|
||||
field
|
||||
.type_options
|
||||
.insert(field_type.to_string(), type_option_data.clone());
|
||||
@ -932,11 +932,12 @@ impl DatabaseEditor {
|
||||
|
||||
pub async fn group_by_field(&self, view_id: &str, field_id: &str) -> FlowyResult<()> {
|
||||
let view = self.database_views.get_view_editor(view_id).await?;
|
||||
view.v_update_grouping_field(field_id).await?;
|
||||
view.v_grouping_by_field(field_id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn set_layout_setting(&self, view_id: &str, layout_setting: LayoutSettingParams) {
|
||||
tracing::trace!("set_layout_setting: {:?}", layout_setting);
|
||||
if let Ok(view) = self.database_views.get_view_editor(view_id).await {
|
||||
let _ = view.v_set_layout_settings(layout_setting).await;
|
||||
};
|
||||
@ -1042,7 +1043,7 @@ impl DatabaseEditor {
|
||||
.await
|
||||
.ok_or_else(FlowyError::record_not_found)?;
|
||||
let rows = database_view.v_get_rows().await;
|
||||
let (database_id, fields) = {
|
||||
let (database_id, fields, is_linked) = {
|
||||
let database = self.database.lock();
|
||||
let database_id = database.get_database_id();
|
||||
let fields = database
|
||||
@ -1051,7 +1052,8 @@ impl DatabaseEditor {
|
||||
.into_iter()
|
||||
.map(FieldIdPB::from)
|
||||
.collect();
|
||||
(database_id, fields)
|
||||
let is_linked = database.is_inline_view(view_id);
|
||||
(database_id, fields, is_linked)
|
||||
};
|
||||
|
||||
let rows = rows
|
||||
@ -1063,6 +1065,7 @@ impl DatabaseEditor {
|
||||
fields,
|
||||
rows,
|
||||
layout_type: view.layout.into(),
|
||||
is_linked,
|
||||
})
|
||||
}
|
||||
|
||||
@ -1082,7 +1085,7 @@ impl DatabaseEditor {
|
||||
self
|
||||
.database
|
||||
.lock()
|
||||
.get_fields(view_id, None)
|
||||
.get_fields_in_view(view_id, None)
|
||||
.into_iter()
|
||||
.filter(|f| FieldType::from(f.field_type).is_auto_update())
|
||||
.collect::<Vec<Field>>()
|
||||
@ -1139,13 +1142,17 @@ struct DatabaseViewDataImpl {
|
||||
}
|
||||
|
||||
impl DatabaseViewData for DatabaseViewDataImpl {
|
||||
fn get_database(&self) -> Arc<InnerDatabase> {
|
||||
self.database.lock().clone()
|
||||
}
|
||||
|
||||
fn get_view(&self, view_id: &str) -> Fut<Option<DatabaseView>> {
|
||||
let view = self.database.lock().get_view(view_id);
|
||||
to_fut(async move { view })
|
||||
}
|
||||
|
||||
fn get_fields(&self, view_id: &str, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<Field>>> {
|
||||
let fields = self.database.lock().get_fields(view_id, field_ids);
|
||||
let fields = self.database.lock().get_fields_in_view(view_id, field_ids);
|
||||
to_fut(async move { fields.into_iter().map(Arc::new).collect() })
|
||||
}
|
||||
|
||||
@ -1166,7 +1173,7 @@ impl DatabaseViewData for DatabaseViewDataImpl {
|
||||
field_type: FieldType,
|
||||
type_option_data: TypeOptionData,
|
||||
) -> Fut<Field> {
|
||||
let (_, field) = self.database.lock().create_default_field(
|
||||
let (_, field) = self.database.lock().create_field_with_mut(
|
||||
view_id,
|
||||
name.to_string(),
|
||||
field_type.clone().into(),
|
||||
|
@ -0,0 +1,115 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use collab_database::database::{gen_field_id, Database};
|
||||
use collab_database::fields::Field;
|
||||
use collab_database::views::{DatabaseLayout, LayoutSetting};
|
||||
|
||||
use crate::entities::FieldType;
|
||||
use crate::services::field::DateTypeOption;
|
||||
use crate::services::setting::CalendarLayoutSetting;
|
||||
|
||||
/// When creating a database, we need to resolve the dependencies of the views. Different database
|
||||
/// view has different dependencies. For example, a calendar view depends on a date field.
|
||||
pub struct DatabaseLayoutDepsResolver {
|
||||
pub database: Arc<Database>,
|
||||
/// The new database layout.
|
||||
pub database_layout: DatabaseLayout,
|
||||
}
|
||||
|
||||
impl DatabaseLayoutDepsResolver {
|
||||
pub fn new(database: Arc<Database>, database_layout: DatabaseLayout) -> Self {
|
||||
Self {
|
||||
database,
|
||||
database_layout,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_deps_when_create_database_linked_view(&self) -> Option<(Field, LayoutSetting)> {
|
||||
match self.database_layout {
|
||||
DatabaseLayout::Grid => None,
|
||||
DatabaseLayout::Board => None,
|
||||
DatabaseLayout::Calendar => {
|
||||
let field = self.create_date_field();
|
||||
let layout_setting: LayoutSetting = CalendarLayoutSetting::new(field.id.clone()).into();
|
||||
Some((field, layout_setting))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn resolve_deps_when_update_layout_type(&self, view_id: &str) {
|
||||
let fields = self.database.get_fields(None);
|
||||
// Insert the layout setting if it's not exist
|
||||
match &self.database_layout {
|
||||
DatabaseLayout::Grid => {},
|
||||
DatabaseLayout::Board => {},
|
||||
DatabaseLayout::Calendar => {
|
||||
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 field = self.create_date_field();
|
||||
let field_id = field.id.clone();
|
||||
self.database.create_field(field);
|
||||
field_id
|
||||
},
|
||||
Some(date_field) => date_field.id,
|
||||
};
|
||||
self.create_calendar_layout_setting_if_need(view_id, &date_field_id);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn create_calendar_layout_setting_if_need(&self, view_id: &str, field_id: &str) {
|
||||
if self
|
||||
.database
|
||||
.get_layout_setting::<CalendarLayoutSetting>(view_id, &self.database_layout)
|
||||
.is_none()
|
||||
{
|
||||
let layout_setting = CalendarLayoutSetting::new(field_id.to_string());
|
||||
self
|
||||
.database
|
||||
.insert_layout_setting(view_id, &self.database_layout, layout_setting);
|
||||
}
|
||||
}
|
||||
|
||||
fn create_date_field(&self) -> Field {
|
||||
let field_type = FieldType::DateTime;
|
||||
let default_date_type_option = DateTypeOption::default();
|
||||
let field_id = gen_field_id();
|
||||
Field::new(
|
||||
field_id,
|
||||
"Date".to_string(),
|
||||
field_type.clone().into(),
|
||||
false,
|
||||
)
|
||||
.with_type_option_data(field_type, default_date_type_option.into())
|
||||
}
|
||||
}
|
||||
|
||||
// pub async fn v_get_layout_settings(&self, layout_ty: &DatabaseLayout) -> LayoutSettingParams {
|
||||
// let mut layout_setting = LayoutSettingParams::default();
|
||||
// match layout_ty {
|
||||
// DatabaseLayout::Grid => {},
|
||||
// DatabaseLayout::Board => {},
|
||||
// DatabaseLayout::Calendar => {
|
||||
// if let Some(value) = self.delegate.get_layout_setting(&self.view_id, layout_ty) {
|
||||
// let calendar_setting = CalendarLayoutSetting::from(value);
|
||||
// // Check the field exist or not
|
||||
// if let Some(field) = self.delegate.get_field(&calendar_setting.field_id).await {
|
||||
// let field_type = FieldType::from(field.field_type);
|
||||
//
|
||||
// // Check the type of field is Datetime or not
|
||||
// if field_type == FieldType::DateTime {
|
||||
// layout_setting.calendar = Some(calendar_setting);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// layout_setting
|
||||
// }
|
@ -1,3 +1,9 @@
|
||||
pub use layout_deps::*;
|
||||
pub use notifier::*;
|
||||
pub use view_editor::*;
|
||||
pub use views::*;
|
||||
|
||||
mod layout_deps;
|
||||
mod notifier;
|
||||
mod view_editor;
|
||||
mod view_filter;
|
||||
@ -5,7 +11,3 @@ mod view_group;
|
||||
mod view_sort;
|
||||
mod views;
|
||||
// mod trait_impl;
|
||||
|
||||
pub use notifier::*;
|
||||
pub use view_editor::*;
|
||||
pub use views::*;
|
||||
|
@ -2,7 +2,7 @@ use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use collab_database::database::{gen_database_filter_id, gen_database_sort_id};
|
||||
use collab_database::database::{gen_database_filter_id, gen_database_sort_id, Database};
|
||||
use collab_database::fields::{Field, TypeOptionData};
|
||||
use collab_database::rows::{Cells, Row, RowCell, RowId, RowMeta};
|
||||
use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting};
|
||||
@ -30,10 +30,10 @@ use crate::services::database_view::view_group::{
|
||||
use crate::services::database_view::view_sort::make_sort_controller;
|
||||
use crate::services::database_view::{
|
||||
notify_did_update_filter, notify_did_update_group_rows, notify_did_update_num_of_groups,
|
||||
notify_did_update_setting, notify_did_update_sort, DatabaseViewChangedNotifier,
|
||||
DatabaseViewChangedReceiverRunner,
|
||||
notify_did_update_setting, notify_did_update_sort, DatabaseLayoutDepsResolver,
|
||||
DatabaseViewChangedNotifier, DatabaseViewChangedReceiverRunner,
|
||||
};
|
||||
use crate::services::field::{DateTypeOption, TypeOptionCellDataHandler};
|
||||
use crate::services::field::TypeOptionCellDataHandler;
|
||||
use crate::services::filter::{
|
||||
Filter, FilterChangeset, FilterController, FilterType, UpdatedFilterType,
|
||||
};
|
||||
@ -44,6 +44,8 @@ use crate::services::setting::CalendarLayoutSetting;
|
||||
use crate::services::sort::{DeletedSortType, Sort, SortChangeset, SortController, SortType};
|
||||
|
||||
pub trait DatabaseViewData: Send + Sync + 'static {
|
||||
fn get_database(&self) -> Arc<Database>;
|
||||
|
||||
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>>>;
|
||||
@ -438,7 +440,7 @@ impl DatabaseViewEditor {
|
||||
pub async fn v_initialize_new_group(&self, field_id: &str) -> FlowyResult<()> {
|
||||
let is_grouping_field = self.is_grouping_field(field_id).await;
|
||||
if !is_grouping_field {
|
||||
self.v_update_grouping_field(field_id).await?;
|
||||
self.v_grouping_by_field(field_id).await?;
|
||||
|
||||
if let Some(view) = self.delegate.get_view(&self.view_id).await {
|
||||
let setting = database_view_setting_pb_from_view(view);
|
||||
@ -607,7 +609,11 @@ impl DatabaseViewEditor {
|
||||
// Check the type of field is Datetime or not
|
||||
if field_type == FieldType::DateTime {
|
||||
layout_setting.calendar = Some(calendar_setting);
|
||||
} else {
|
||||
tracing::warn!("The field of calendar setting is not datetime type")
|
||||
}
|
||||
} else {
|
||||
tracing::warn!("The field of calendar setting is not exist");
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -713,7 +719,7 @@ impl DatabaseViewEditor {
|
||||
|
||||
/// Called when a grouping field is updated.
|
||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||
pub async fn v_update_grouping_field(&self, field_id: &str) -> FlowyResult<()> {
|
||||
pub async fn v_grouping_by_field(&self, field_id: &str) -> FlowyResult<()> {
|
||||
if let Some(field) = self.delegate.get_field(field_id).await {
|
||||
let new_group_controller =
|
||||
new_group_controller_with_field(self.view_id.clone(), self.delegate.clone(), field).await?;
|
||||
@ -770,12 +776,23 @@ impl DatabaseViewEditor {
|
||||
date_field_id: date_field.id.clone(),
|
||||
title,
|
||||
timestamp,
|
||||
is_scheduled: timestamp != 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn v_get_all_calendar_events(&self) -> Option<Vec<CalendarEventPB>> {
|
||||
let layout_ty = DatabaseLayout::Calendar;
|
||||
let calendar_setting = self.v_get_layout_settings(&layout_ty).await.calendar?;
|
||||
let calendar_setting = match self.v_get_layout_settings(&layout_ty).await.calendar {
|
||||
None => {
|
||||
// When create a new calendar view, the calendar setting should be created
|
||||
tracing::error!(
|
||||
"Calendar layout setting not found in database view:{}",
|
||||
self.view_id
|
||||
);
|
||||
return None;
|
||||
},
|
||||
Some(calendar_setting) => calendar_setting,
|
||||
};
|
||||
|
||||
// Text
|
||||
let primary_field = self.delegate.get_primary_field().await?;
|
||||
@ -822,6 +839,7 @@ impl DatabaseViewEditor {
|
||||
date_field_id: calendar_setting.field_id.clone(),
|
||||
title,
|
||||
timestamp,
|
||||
is_scheduled: timestamp != 0,
|
||||
};
|
||||
events.push(event);
|
||||
}
|
||||
@ -829,57 +847,25 @@ impl DatabaseViewEditor {
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
pub async fn v_update_layout_type(&self, layout_type: DatabaseLayout) -> FlowyResult<()> {
|
||||
pub async fn v_update_layout_type(&self, new_layout_type: DatabaseLayout) -> FlowyResult<()> {
|
||||
self
|
||||
.delegate
|
||||
.update_layout_type(&self.view_id, &layout_type);
|
||||
.update_layout_type(&self.view_id, &new_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)
|
||||
// using the {} brackets to denote the lifetime of the resolver. Because the DatabaseLayoutDepsResolver
|
||||
// is not sync and send, so we can't pass it to the async block.
|
||||
{
|
||||
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 resolver = DatabaseLayoutDepsResolver::new(self.delegate.get_database(), new_layout_type);
|
||||
resolver.resolve_deps_when_update_layout_type(&self.view_id);
|
||||
}
|
||||
|
||||
// initialize the group controller if the current layout support grouping
|
||||
*self.group_controller.write().await =
|
||||
new_group_controller(self.view_id.clone(), self.delegate.clone()).await?;
|
||||
|
||||
let payload = DatabaseLayoutMetaPB {
|
||||
view_id: self.view_id.clone(),
|
||||
layout: layout_type.into(),
|
||||
layout: new_layout_type.into(),
|
||||
};
|
||||
send_notification(&self.view_id, DatabaseNotification::DidUpdateDatabaseLayout)
|
||||
.payload(payload)
|
||||
|
@ -94,7 +94,7 @@ impl DatabaseViews {
|
||||
// If the id of the grouping field is equal to the updated field's id, then we need to
|
||||
// update the group setting
|
||||
if view_editor.is_grouping_field(field_id).await {
|
||||
view_editor.v_update_grouping_field(field_id).await?;
|
||||
view_editor.v_grouping_by_field(field_id).await?;
|
||||
}
|
||||
view_editor
|
||||
.v_did_update_field_type_option(field_id, old_field)
|
||||
|
@ -1,18 +1,21 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Formatter;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
|
||||
use collab_database::fields::Field;
|
||||
use indexmap::IndexMap;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use lib_infra::future::Fut;
|
||||
|
||||
use crate::entities::{GroupChangesPB, GroupPB, InsertedGroupPB};
|
||||
use crate::services::field::RowSingleCellData;
|
||||
use crate::services::group::{
|
||||
default_group_setting, GeneratedGroups, Group, GroupChangeset, GroupData, GroupSetting,
|
||||
};
|
||||
use collab_database::fields::Field;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use indexmap::IndexMap;
|
||||
use lib_infra::future::Fut;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Formatter;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub trait GroupSettingReader: Send + Sync + 'static {
|
||||
fn get_group_setting(&self, view_id: &str) -> Fut<Option<Arc<GroupSetting>>>;
|
||||
@ -361,10 +364,10 @@ where
|
||||
})?;
|
||||
|
||||
if let Some(group) = update_group {
|
||||
self.group_by_id.get_mut(&group.id).map(|group_data| {
|
||||
if let Some(group_data) = self.group_by_id.get_mut(&group.id) {
|
||||
group_data.name = group.name.clone();
|
||||
group_data.is_visible = group.visible;
|
||||
});
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
use crate::entities::FieldType;
|
||||
use crate::services::cell::stringify_cell_data;
|
||||
use collab_database::database::Database;
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use crate::entities::FieldType;
|
||||
use crate::services::cell::stringify_cell_data;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum CSVFormat {
|
||||
@ -20,7 +21,7 @@ impl CSVExport {
|
||||
pub fn export_database(&self, database: &Database, style: CSVFormat) -> FlowyResult<String> {
|
||||
let mut wtr = csv::Writer::from_writer(vec![]);
|
||||
let inline_view_id = database.get_inline_view_id();
|
||||
let fields = database.get_fields(&inline_view_id, None);
|
||||
let fields = database.get_fields_in_view(&inline_view_id, None);
|
||||
|
||||
// Write fields
|
||||
let field_records = fields
|
||||
|
Reference in New Issue
Block a user