feat: add basic relation field (#4397)

* feat: add basic relation field

* fix: clippy

* fix: tauri build 🤞

* chore: merge changes

* fix: merge main

* chore: initial code review pass

* fix: rust-lib test

* chore: code cleanup

* fix: unwrap or default
This commit is contained in:
Richard Shiue
2024-02-29 14:38:18 +08:00
committed by GitHub
parent f826d05f03
commit f4ca3ef782
54 changed files with 1804 additions and 34 deletions

View File

@ -69,6 +69,12 @@ impl AsRef<str> for DatabaseIdPB {
}
}
#[derive(Clone, ProtoBuf, Default, Debug)]
pub struct RepeatedDatabaseIdPB {
#[pb(index = 1)]
pub value: Vec<DatabaseIdPB>,
}
#[derive(Clone, ProtoBuf, Default, Debug, Validate)]
pub struct DatabaseViewIdPB {
#[pb(index = 1)]

View File

@ -473,6 +473,7 @@ pub enum FieldType {
Checklist = 7,
LastEditedTime = 8,
CreatedTime = 9,
Relation = 10,
}
impl Display for FieldType {
@ -509,8 +510,9 @@ impl FieldType {
FieldType::Checkbox => "Checkbox",
FieldType::URL => "URL",
FieldType::Checklist => "Checklist",
FieldType::LastEditedTime => "Last edited time",
FieldType::LastEditedTime => "Last modified",
FieldType::CreatedTime => "Created time",
FieldType::Relation => "Relation",
};
s.to_string()
}
@ -559,6 +561,10 @@ impl FieldType {
matches!(self, FieldType::Checklist)
}
pub fn is_relation(&self) -> bool {
matches!(self, FieldType::Relation)
}
pub fn can_be_group(&self) -> bool {
self.is_select_option() || self.is_checkbox() || self.is_url()
}

View File

@ -3,6 +3,7 @@ mod checklist_filter;
mod date_filter;
mod filter_changeset;
mod number_filter;
mod relation_filter;
mod select_option_filter;
mod text_filter;
mod util;
@ -12,6 +13,7 @@ pub use checklist_filter::*;
pub use date_filter::*;
pub use filter_changeset::*;
pub use number_filter::*;
pub use relation_filter::*;
pub use select_option_filter::*;
pub use text_filter::*;
pub use util::*;

View File

@ -0,0 +1,24 @@
use flowy_derive::ProtoBuf;
use crate::services::filter::{Filter, FromFilterString};
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
pub struct RelationFilterPB {
#[pb(index = 1)]
pub condition: i64,
}
impl FromFilterString for RelationFilterPB {
fn from_filter(_filter: &Filter) -> Self
where
Self: Sized,
{
RelationFilterPB { condition: 0 }
}
}
impl From<&Filter> for RelationFilterPB {
fn from(_filter: &Filter) -> Self {
RelationFilterPB { condition: 0 }
}
}

View File

@ -11,7 +11,7 @@ use validator::Validate;
use crate::entities::parser::NotEmptyStr;
use crate::entities::{
CheckboxFilterPB, ChecklistFilterPB, DateFilterContentPB, DateFilterPB, FieldType,
NumberFilterPB, SelectOptionFilterPB, TextFilterPB,
NumberFilterPB, RelationFilterPB, SelectOptionFilterPB, TextFilterPB,
};
use crate::services::field::SelectOptionIds;
use crate::services::filter::Filter;
@ -44,6 +44,7 @@ impl std::convert::From<&Filter> for FilterPB {
FieldType::Checklist => ChecklistFilterPB::from(filter).try_into().unwrap(),
FieldType::Checkbox => CheckboxFilterPB::from(filter).try_into().unwrap(),
FieldType::URL => TextFilterPB::from(filter).try_into().unwrap(),
FieldType::Relation => RelationFilterPB::from(filter).try_into().unwrap(),
};
Self {
id: filter.id.clone(),
@ -186,6 +187,10 @@ impl TryInto<UpdateFilterParams> for UpdateFilterPayloadPB {
condition = filter.condition as u8;
content = SelectOptionIds::from(filter.option_ids).to_string();
},
FieldType::Relation => {
let filter = RelationFilterPB::try_from(bytes).map_err(|_| ErrorCode::ProtobufSerde)?;
condition = filter.condition as u8;
},
}
Ok(UpdateFilterParams {

View File

@ -14,8 +14,9 @@ macro_rules! impl_into_field_type {
7 => FieldType::Checklist,
8 => FieldType::LastEditedTime,
9 => FieldType::CreatedTime,
10 => FieldType::Relation,
_ => {
tracing::error!("🔴Can't parser FieldType from value: {}", ty);
tracing::error!("🔴Can't parse FieldType from value: {}", ty);
FieldType::RichText
},
}
@ -34,7 +35,7 @@ macro_rules! impl_into_field_visibility {
1 => FieldVisibility::HideWhenEmpty,
2 => FieldVisibility::AlwaysHidden,
_ => {
tracing::error!("🔴Can't parser FieldVisibility from value: {}", ty);
tracing::error!("🔴Can't parse FieldVisibility from value: {}", ty);
FieldVisibility::AlwaysShown
},
}

View File

@ -2,6 +2,7 @@ mod checkbox_entities;
mod checklist_entities;
mod date_entities;
mod number_entities;
mod relation_entities;
mod select_option_entities;
mod text_entities;
mod timestamp_entities;
@ -11,6 +12,7 @@ pub use checkbox_entities::*;
pub use checklist_entities::*;
pub use date_entities::*;
pub use number_entities::*;
pub use relation_entities::*;
pub use select_option_entities::*;
pub use text_entities::*;
pub use timestamp_entities::*;

View File

@ -0,0 +1,87 @@
use flowy_derive::ProtoBuf;
use crate::entities::CellIdPB;
use crate::services::field::{RelationCellData, RelationTypeOption};
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct RelationCellDataPB {
#[pb(index = 1)]
pub row_ids: Vec<String>,
}
impl From<RelationCellData> for RelationCellDataPB {
fn from(data: RelationCellData) -> Self {
Self {
row_ids: data.row_ids.into_iter().map(Into::into).collect(),
}
}
}
impl From<RelationCellDataPB> for RelationCellData {
fn from(data: RelationCellDataPB) -> Self {
Self {
row_ids: data.row_ids.into_iter().map(Into::into).collect(),
}
}
}
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct RelationCellChangesetPB {
#[pb(index = 1)]
pub view_id: String,
#[pb(index = 2)]
pub cell_id: CellIdPB,
#[pb(index = 3)]
pub inserted_row_ids: Vec<String>,
#[pb(index = 4)]
pub removed_row_ids: Vec<String>,
}
#[derive(Clone, Debug, Default, ProtoBuf)]
pub struct RelationTypeOptionPB {
#[pb(index = 1)]
pub database_id: String,
}
impl From<RelationTypeOption> for RelationTypeOptionPB {
fn from(value: RelationTypeOption) -> Self {
RelationTypeOptionPB {
database_id: value.database_id,
}
}
}
impl From<RelationTypeOptionPB> for RelationTypeOption {
fn from(value: RelationTypeOptionPB) -> Self {
RelationTypeOption {
database_id: value.database_id,
}
}
}
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct RelatedRowDataPB {
#[pb(index = 1)]
pub row_id: String,
#[pb(index = 2)]
pub name: String,
}
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct RepeatedRelatedRowDataPB {
#[pb(index = 1)]
pub rows: Vec<RelatedRowDataPB>,
}
#[derive(Debug, Default, Clone, ProtoBuf)]
pub struct RepeatedRowIdPB {
#[pb(index = 1)]
pub database_id: String,
#[pb(index = 2)]
pub row_ids: Vec<String>,
}

View File

@ -13,7 +13,8 @@ use crate::entities::*;
use crate::manager::DatabaseManager;
use crate::services::cell::CellBuilder;
use crate::services::field::{
type_option_data_from_pb, ChecklistCellChangeset, DateCellChangeset, SelectOptionCellChangeset,
type_option_data_from_pb, ChecklistCellChangeset, DateCellChangeset, RelationCellChangeset,
SelectOptionCellChangeset,
};
use crate::services::field_settings::FieldSettingsChangesetParams;
use crate::services::group::GroupChangeset;
@ -978,3 +979,81 @@ pub(crate) async fn remove_calculation_handler(
Ok(())
}
pub(crate) async fn get_related_database_ids_handler(
_data: AFPluginData<DatabaseViewIdPB>,
_manager: AFPluginState<Weak<DatabaseManager>>,
) -> FlowyResult<()> {
Ok(())
}
#[tracing::instrument(level = "debug", skip_all, err)]
pub(crate) async fn update_relation_cell_handler(
data: AFPluginData<RelationCellChangesetPB>,
manager: AFPluginState<Weak<DatabaseManager>>,
) -> FlowyResult<()> {
let manager = upgrade_manager(manager)?;
let params: RelationCellChangesetPB = data.into_inner();
let view_id = parser::NotEmptyStr::parse(params.view_id)
.map_err(|_| flowy_error::ErrorCode::DatabaseViewIdIsEmpty)?
.0;
let cell_id: CellIdParams = params.cell_id.try_into()?;
let params = RelationCellChangeset {
inserted_row_ids: params
.inserted_row_ids
.into_iter()
.map(Into::into)
.collect(),
removed_row_ids: params.removed_row_ids.into_iter().map(Into::into).collect(),
};
let database_editor = manager.get_database_with_view_id(&view_id).await?;
// // get the related database
// let related_database_id = database_editor
// .get_related_database_id(&cell_id.field_id)
// .await?;
// let related_database_editor = manager.get_database(&related_database_id).await?;
// // validate the changeset contents
// related_database_editor
// .validate_row_ids_exist(&params)
// .await?;
// update the cell in the database
database_editor
.update_cell_with_changeset(
&view_id,
cell_id.row_id,
&cell_id.field_id,
BoxAny::new(params),
)
.await?;
Ok(())
}
pub(crate) async fn get_related_row_datas_handler(
data: AFPluginData<RepeatedRowIdPB>,
manager: AFPluginState<Weak<DatabaseManager>>,
) -> DataResult<RepeatedRelatedRowDataPB, FlowyError> {
let manager = upgrade_manager(manager)?;
let params: RepeatedRowIdPB = data.into_inner();
let database_editor = manager.get_database(&params.database_id).await?;
let row_datas = database_editor
.get_related_rows(Some(&params.row_ids))
.await?;
data_result_ok(RepeatedRelatedRowDataPB { rows: row_datas })
}
pub(crate) async fn get_related_database_rows_handler(
data: AFPluginData<DatabaseIdPB>,
manager: AFPluginState<Weak<DatabaseManager>>,
) -> DataResult<RepeatedRelatedRowDataPB, FlowyError> {
let manager = upgrade_manager(manager)?;
let database_id = data.into_inner().value;
let database_editor = manager.get_database(&database_id).await?;
let row_datas = database_editor.get_related_rows(None).await?;
data_result_ok(RepeatedRelatedRowDataPB { rows: row_datas })
}

View File

@ -83,6 +83,11 @@ pub fn init(database_manager: Weak<DatabaseManager>) -> AFPlugin {
.event(DatabaseEvent::GetAllCalculations, get_all_calculations_handler)
.event(DatabaseEvent::UpdateCalculation, update_calculation_handler)
.event(DatabaseEvent::RemoveCalculation, remove_calculation_handler)
// Relation
.event(DatabaseEvent::GetRelatedDatabaseIds, get_related_database_ids_handler)
.event(DatabaseEvent::UpdateRelationCell, update_relation_cell_handler)
.event(DatabaseEvent::GetRelatedRowDatas, get_related_row_datas_handler)
.event(DatabaseEvent::GetRelatedDatabaseRows, get_related_database_rows_handler)
}
/// [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)
@ -342,4 +347,22 @@ pub enum DatabaseEvent {
#[event(input = "RemoveCalculationChangesetPB")]
RemoveCalculation = 165,
/// Currently unused. Get a list of database ids that this database relates
/// to.
#[event(input = "DatabaseViewIdPB", output = "RepeatedDatabaseIdPB")]
GetRelatedDatabaseIds = 170,
/// Updates a relation cell, adding or removing links to rows in another
/// database
#[event(input = "RelationCellChangesetPB")]
UpdateRelationCell = 171,
/// Get the names of the linked rows in a relation cell.
#[event(input = "RepeatedRowIdPB", output = "RepeatedRelatedRowDataPB")]
GetRelatedRowDatas = 172,
/// Get the names of all the rows in a related database.
#[event(input = "DatabaseIdPB", output = "RepeatedRelatedRowDataPB")]
GetRelatedDatabaseRows = 173,
}

View File

@ -307,6 +307,9 @@ impl<'a> CellBuilder<'a> {
cells.insert(field_id, insert_select_option_cell(ids.into_inner(), field));
}
},
FieldType::Relation => {
cells.insert(field_id, (&RelationCellData::from(cell_str)).into());
},
}
}
}

View File

@ -26,8 +26,8 @@ use crate::services::database_view::{
};
use crate::services::field::{
default_type_option_data_from_type, select_type_option_from_field, transform_type_option,
type_option_data_from_pb, ChecklistCellChangeset, SelectOptionCellChangeset, SelectOptionIds,
TimestampCellData, TypeOptionCellDataHandler, TypeOptionCellExt,
type_option_data_from_pb, ChecklistCellChangeset, RelationTypeOption, SelectOptionCellChangeset,
SelectOptionIds, StrCellData, TimestampCellData, TypeOptionCellDataHandler, TypeOptionCellExt,
};
use crate::services::field_settings::{
default_field_settings_by_layout_map, FieldSettings, FieldSettingsChangesetParams,
@ -1238,6 +1238,61 @@ impl DatabaseEditor {
Ok(())
}
pub async fn get_related_database_id(&self, field_id: &str) -> FlowyResult<String> {
let mut field = self
.database
.lock()
.get_fields(Some(vec![field_id.to_string()]));
let field = field.pop().ok_or(FlowyError::internal())?;
let type_option = field
.get_type_option::<RelationTypeOption>(FieldType::Relation)
.ok_or(FlowyError::record_not_found())?;
Ok(type_option.database_id)
}
pub async fn get_related_rows(
&self,
row_ids: Option<&Vec<String>>,
) -> FlowyResult<Vec<RelatedRowDataPB>> {
let primary_field = self.database.lock().fields.get_primary_field().unwrap();
let handler =
TypeOptionCellExt::new_with_cell_data_cache(&primary_field, Some(self.cell_cache.clone()))
.get_type_option_cell_data_handler(&FieldType::RichText)
.ok_or(FlowyError::internal())?;
let row_data = {
let database = self.database.lock();
let mut rows = database.get_database_rows();
if let Some(row_ids) = row_ids {
rows.retain(|row| row_ids.contains(&row.id));
}
rows
.iter()
.map(|row| {
let title = database
.get_cell(&primary_field.id, &row.id)
.cell
.and_then(|cell| {
handler
.get_cell_data(&cell, &FieldType::RichText, &primary_field)
.ok()
})
.and_then(|cell_data| cell_data.unbox_or_none())
.unwrap_or_else(|| StrCellData("".to_string()));
RelatedRowDataPB {
row_id: row.id.to_string(),
name: title.0,
}
})
.collect::<Vec<_>>()
};
Ok(row_data)
}
fn get_auto_updated_fields(&self, view_id: &str) -> Vec<Field> {
self
.database

View File

@ -1,6 +1,6 @@
mod field_builder;
mod field_operation;
mod type_options;
pub mod type_options;
pub use field_builder::*;
pub use field_operation::*;

View File

@ -2,6 +2,7 @@ pub mod checkbox_type_option;
pub mod checklist_type_option;
pub mod date_type_option;
pub mod number_type_option;
pub mod relation_type_option;
pub mod selection_type_option;
pub mod text_type_option;
pub mod timestamp_type_option;
@ -14,6 +15,7 @@ pub use checkbox_type_option::*;
pub use checklist_type_option::*;
pub use date_type_option::*;
pub use number_type_option::*;
pub use relation_type_option::*;
pub use selection_type_option::*;
pub use text_type_option::*;
pub use timestamp_type_option::*;

View File

@ -0,0 +1,5 @@
mod relation;
mod relation_entities;
pub use relation::*;
pub use relation_entities::*;

View File

@ -0,0 +1,159 @@
use std::cmp::Ordering;
use collab::core::any_map::AnyMapExtension;
use collab_database::fields::{Field, TypeOptionData, TypeOptionDataBuilder};
use collab_database::rows::Cell;
use flowy_error::FlowyResult;
use serde::{Deserialize, Serialize};
use crate::entities::{FieldType, RelationCellDataPB, RelationFilterPB};
use crate::services::cell::{CellDataChangeset, CellDataDecoder};
use crate::services::field::{
default_order, TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter,
TypeOptionCellDataSerde, TypeOptionTransform,
};
use crate::services::sort::SortCondition;
use super::{RelationCellChangeset, RelationCellData};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct RelationTypeOption {
pub database_id: String,
}
impl From<TypeOptionData> for RelationTypeOption {
fn from(value: TypeOptionData) -> Self {
let database_id = value.get_str_value("database_id").unwrap_or_default();
Self { database_id }
}
}
impl From<RelationTypeOption> for TypeOptionData {
fn from(value: RelationTypeOption) -> Self {
TypeOptionDataBuilder::new()
.insert_str_value("database_id", value.database_id)
.build()
}
}
impl TypeOption for RelationTypeOption {
type CellData = RelationCellData;
type CellChangeset = RelationCellChangeset;
type CellProtobufType = RelationCellDataPB;
type CellFilter = RelationFilterPB;
}
impl CellDataChangeset for RelationTypeOption {
fn apply_changeset(
&self,
changeset: RelationCellChangeset,
cell: Option<Cell>,
) -> FlowyResult<(Cell, RelationCellData)> {
if cell.is_none() {
let cell_data = RelationCellData {
row_ids: changeset.inserted_row_ids,
};
return Ok(((&cell_data).into(), cell_data));
}
let cell_data: RelationCellData = cell.unwrap().as_ref().into();
let mut row_ids = cell_data.row_ids.clone();
for inserted in changeset.inserted_row_ids.iter() {
if row_ids.iter().any(|row_id| row_id == inserted) {
row_ids.push(inserted.clone())
}
}
for removed_id in changeset.removed_row_ids.iter() {
if let Some(index) = row_ids.iter().position(|row_id| row_id == removed_id) {
row_ids.remove(index);
}
}
let cell_data = RelationCellData { row_ids };
Ok(((&cell_data).into(), cell_data))
}
}
impl CellDataDecoder for RelationTypeOption {
fn decode_cell(
&self,
cell: &Cell,
decoded_field_type: &FieldType,
_field: &Field,
) -> FlowyResult<RelationCellData> {
if !decoded_field_type.is_relation() {
return Ok(Default::default());
}
Ok(cell.into())
}
fn stringify_cell_data(&self, cell_data: RelationCellData) -> String {
cell_data.to_string()
}
fn stringify_cell(&self, cell: &Cell) -> String {
let cell_data = RelationCellData::from(cell);
cell_data.to_string()
}
fn numeric_cell(&self, _cell: &Cell) -> Option<f64> {
None
}
}
impl TypeOptionCellDataCompare for RelationTypeOption {
fn apply_cmp(
&self,
_cell_data: &RelationCellData,
_other_cell_data: &RelationCellData,
_sort_condition: SortCondition,
) -> Ordering {
default_order()
}
}
impl TypeOptionCellDataFilter for RelationTypeOption {
fn apply_filter(
&self,
_filter: &RelationFilterPB,
_field_type: &FieldType,
_cell_data: &RelationCellData,
) -> bool {
true
}
}
impl TypeOptionTransform for RelationTypeOption {
fn transformable(&self) -> bool {
false
}
fn transform_type_option(
&mut self,
_old_type_option_field_type: FieldType,
_old_type_option_data: TypeOptionData,
) {
}
fn transform_type_option_cell(
&self,
_cell: &Cell,
_transformed_field_type: &FieldType,
_field: &Field,
) -> Option<RelationCellData> {
None
}
}
impl TypeOptionCellDataSerde for RelationTypeOption {
fn protobuf_encode(&self, cell_data: RelationCellData) -> RelationCellDataPB {
cell_data.into()
}
fn parse_cell(&self, cell: &Cell) -> FlowyResult<RelationCellData> {
Ok(cell.into())
}
}

View File

@ -0,0 +1,85 @@
use std::sync::Arc;
use collab::preclude::Any;
use collab_database::rows::{new_cell_builder, Cell, RowId};
use crate::entities::FieldType;
use crate::services::field::{TypeOptionCellData, CELL_DATA};
#[derive(Debug, Clone, Default)]
pub struct RelationCellData {
pub row_ids: Vec<RowId>,
}
impl From<&Cell> for RelationCellData {
fn from(value: &Cell) -> Self {
let row_ids = match value.get(CELL_DATA) {
Some(Any::Array(array)) => array
.iter()
.flat_map(|item| {
if let Any::String(string) = item {
Some(RowId::from(string.clone().to_string()))
} else {
None
}
})
.collect(),
_ => vec![],
};
Self { row_ids }
}
}
impl From<&RelationCellData> for Cell {
fn from(value: &RelationCellData) -> Self {
let data = Any::Array(Arc::from(
value
.row_ids
.clone()
.into_iter()
.map(|id| Any::String(Arc::from(id.to_string())))
.collect::<Vec<_>>(),
));
new_cell_builder(FieldType::Relation)
.insert_any(CELL_DATA, data)
.build()
}
}
impl From<String> for RelationCellData {
fn from(s: String) -> Self {
if s.is_empty() {
return RelationCellData { row_ids: vec![] };
}
let ids = s
.split(", ")
.map(|id| id.to_string().into())
.collect::<Vec<_>>();
RelationCellData { row_ids: ids }
}
}
impl TypeOptionCellData for RelationCellData {
fn is_cell_empty(&self) -> bool {
self.row_ids.is_empty()
}
}
impl ToString for RelationCellData {
fn to_string(&self) -> String {
self
.row_ids
.iter()
.map(|id| id.to_string())
.collect::<Vec<_>>()
.join(", ")
}
}
#[derive(Debug, Clone, Default)]
pub struct RelationCellChangeset {
pub inserted_row_ids: Vec<RowId>,
pub removed_row_ids: Vec<RowId>,
}

View File

@ -10,14 +10,14 @@ use flowy_error::FlowyResult;
use crate::entities::{
CheckboxTypeOptionPB, ChecklistTypeOptionPB, DateTypeOptionPB, FieldType,
MultiSelectTypeOptionPB, NumberTypeOptionPB, RichTextTypeOptionPB, SingleSelectTypeOptionPB,
TimestampTypeOptionPB, URLTypeOptionPB,
MultiSelectTypeOptionPB, NumberTypeOptionPB, RelationTypeOptionPB, RichTextTypeOptionPB,
SingleSelectTypeOptionPB, TimestampTypeOptionPB, URLTypeOptionPB,
};
use crate::services::cell::CellDataDecoder;
use crate::services::field::checklist_type_option::ChecklistTypeOption;
use crate::services::field::{
CheckboxTypeOption, DateTypeOption, MultiSelectTypeOption, NumberTypeOption, RichTextTypeOption,
SingleSelectTypeOption, TimestampTypeOption, URLTypeOption,
CheckboxTypeOption, DateTypeOption, MultiSelectTypeOption, NumberTypeOption, RelationTypeOption,
RichTextTypeOption, SingleSelectTypeOption, TimestampTypeOption, URLTypeOption,
};
use crate::services::filter::FromFilterString;
use crate::services::sort::SortCondition;
@ -202,6 +202,9 @@ pub fn type_option_data_from_pb<T: Into<Bytes>>(
FieldType::Checklist => {
ChecklistTypeOptionPB::try_from(bytes).map(|pb| ChecklistTypeOption::from(pb).into())
},
FieldType::Relation => {
RelationTypeOptionPB::try_from(bytes).map(|pb| RelationTypeOption::from(pb).into())
},
}
}
@ -257,6 +260,12 @@ pub fn type_option_to_pb(type_option: TypeOptionData, field_type: &FieldType) ->
.try_into()
.unwrap()
},
FieldType::Relation => {
let relation_type_option: RelationTypeOption = type_option.into();
RelationTypeOptionPB::from(relation_type_option)
.try_into()
.unwrap()
},
}
}
@ -276,5 +285,6 @@ pub fn default_type_option_data_from_type(field_type: FieldType) -> TypeOptionDa
FieldType::Checkbox => CheckboxTypeOption::default().into(),
FieldType::URL => URLTypeOption::default().into(),
FieldType::Checklist => ChecklistTypeOption.into(),
FieldType::Relation => RelationTypeOption::default().into(),
}
}

View File

@ -14,9 +14,10 @@ use crate::services::cell::{
};
use crate::services::field::checklist_type_option::ChecklistTypeOption;
use crate::services::field::{
CheckboxTypeOption, DateTypeOption, MultiSelectTypeOption, NumberTypeOption, RichTextTypeOption,
SingleSelectTypeOption, TimestampTypeOption, TypeOption, TypeOptionCellDataCompare,
TypeOptionCellDataFilter, TypeOptionCellDataSerde, TypeOptionTransform, URLTypeOption,
CheckboxTypeOption, DateTypeOption, MultiSelectTypeOption, NumberTypeOption, RelationTypeOption,
RichTextTypeOption, SingleSelectTypeOption, TimestampTypeOption, TypeOption,
TypeOptionCellDataCompare, TypeOptionCellDataFilter, TypeOptionCellDataSerde,
TypeOptionTransform, URLTypeOption,
};
use crate::services::sort::SortCondition;
@ -490,6 +491,16 @@ impl<'a> TypeOptionCellExt<'a> {
self.cell_data_cache.clone(),
)
}),
FieldType::Relation => self
.field
.get_type_option::<RelationTypeOption>(field_type)
.map(|type_option| {
TypeOptionCellDataHandlerImpl::new_with_boxed(
type_option,
self.cell_filter_cache.clone(),
self.cell_data_cache.clone(),
)
}),
}
}
}
@ -568,6 +579,9 @@ fn get_type_option_transform_handler(
FieldType::Checklist => {
Box::new(ChecklistTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
FieldType::Relation => {
Box::new(RelationTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
},
}
}

View File

@ -358,6 +358,12 @@ impl FilterController {
.write()
.insert(field_id, ChecklistFilterPB::from_filter(filter.as_ref()));
},
FieldType::Relation => {
self
.cell_filter_cache
.write()
.insert(field_id, RelationFilterPB::from_filter(filter.as_ref()));
},
}
}
}