mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: AI translation in Database (#5515)
* chore: add tranlate field type * chore: integrate ai translate * chore: integrate client api * chore: implement UI
This commit is contained in:
@ -449,6 +449,7 @@ pub enum FieldType {
|
||||
CreatedTime = 9,
|
||||
Relation = 10,
|
||||
Summary = 11,
|
||||
Translate = 12,
|
||||
}
|
||||
|
||||
impl Display for FieldType {
|
||||
@ -489,6 +490,7 @@ impl FieldType {
|
||||
FieldType::CreatedTime => "Created time",
|
||||
FieldType::Relation => "Relation",
|
||||
FieldType::Summary => "Summarize",
|
||||
FieldType::Translate => "Translate",
|
||||
};
|
||||
s.to_string()
|
||||
}
|
||||
|
@ -109,6 +109,10 @@ impl From<&Filter> for FilterPB {
|
||||
.cloned::<TextFilterPB>()
|
||||
.unwrap()
|
||||
.try_into(),
|
||||
FieldType::Translate => condition_and_content
|
||||
.cloned::<TextFilterPB>()
|
||||
.unwrap()
|
||||
.try_into(),
|
||||
};
|
||||
|
||||
Self {
|
||||
@ -156,6 +160,9 @@ impl TryFrom<FilterDataPB> for FilterInner {
|
||||
FieldType::Summary => {
|
||||
BoxAny::new(TextFilterPB::try_from(bytes).map_err(|_| ErrorCode::ProtobufSerde)?)
|
||||
},
|
||||
FieldType::Translate => {
|
||||
BoxAny::new(TextFilterPB::try_from(bytes).map_err(|_| ErrorCode::ProtobufSerde)?)
|
||||
},
|
||||
};
|
||||
|
||||
Ok(Self::Data {
|
||||
|
@ -16,6 +16,7 @@ macro_rules! impl_into_field_type {
|
||||
9 => FieldType::CreatedTime,
|
||||
10 => FieldType::Relation,
|
||||
11 => FieldType::Summary,
|
||||
12 => FieldType::Translate,
|
||||
_ => {
|
||||
tracing::error!("🔴Can't parse FieldType from value: {}", ty);
|
||||
FieldType::RichText
|
||||
|
@ -380,3 +380,18 @@ pub struct SummaryRowPB {
|
||||
#[pb(index = 3)]
|
||||
pub field_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, ProtoBuf, Validate)]
|
||||
pub struct TranslateRowPB {
|
||||
#[pb(index = 1)]
|
||||
#[validate(custom = "required_not_empty_str")]
|
||||
pub view_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
#[validate(custom = "required_not_empty_str")]
|
||||
pub row_id: String,
|
||||
|
||||
#[pb(index = 3)]
|
||||
#[validate(custom = "required_not_empty_str")]
|
||||
pub field_id: String,
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ mod select_option_entities;
|
||||
mod summary_entities;
|
||||
mod text_entities;
|
||||
mod timestamp_entities;
|
||||
mod translate_entities;
|
||||
mod url_entities;
|
||||
|
||||
pub use checkbox_entities::*;
|
||||
@ -18,4 +19,5 @@ pub use select_option_entities::*;
|
||||
pub use summary_entities::*;
|
||||
pub use text_entities::*;
|
||||
pub use timestamp_entities::*;
|
||||
pub use translate_entities::*;
|
||||
pub use url_entities::*;
|
||||
|
@ -0,0 +1,50 @@
|
||||
use crate::services::field::translate_type_option::translate::TranslateTypeOption;
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
|
||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||
pub struct TranslateTypeOptionPB {
|
||||
#[pb(index = 1)]
|
||||
pub auto_fill: bool,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub language: TranslateLanguagePB,
|
||||
}
|
||||
|
||||
impl From<TranslateTypeOption> for TranslateTypeOptionPB {
|
||||
fn from(value: TranslateTypeOption) -> Self {
|
||||
TranslateTypeOptionPB {
|
||||
auto_fill: value.auto_fill,
|
||||
language: value.language_type.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TranslateTypeOptionPB> for TranslateTypeOption {
|
||||
fn from(value: TranslateTypeOptionPB) -> Self {
|
||||
TranslateTypeOption {
|
||||
auto_fill: value.auto_fill,
|
||||
language_type: value.language as i64,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, Copy, ProtoBuf_Enum, Default)]
|
||||
#[repr(i64)]
|
||||
pub enum TranslateLanguagePB {
|
||||
Chinese = 0,
|
||||
#[default]
|
||||
English = 1,
|
||||
French = 2,
|
||||
German = 3,
|
||||
}
|
||||
|
||||
impl From<i64> for TranslateLanguagePB {
|
||||
fn from(data: i64) -> Self {
|
||||
match data {
|
||||
0 => TranslateLanguagePB::Chinese,
|
||||
1 => TranslateLanguagePB::English,
|
||||
2 => TranslateLanguagePB::French,
|
||||
3 => TranslateLanguagePB::German,
|
||||
_ => TranslateLanguagePB::English,
|
||||
}
|
||||
}
|
||||
}
|
@ -1104,3 +1104,16 @@ pub(crate) async fn summarize_row_handler(
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn translate_row_handler(
|
||||
data: AFPluginData<TranslateRowPB>,
|
||||
manager: AFPluginState<Weak<DatabaseManager>>,
|
||||
) -> Result<(), FlowyError> {
|
||||
let manager = upgrade_manager(manager)?;
|
||||
let data = data.try_into_inner()?;
|
||||
let row_id = RowId::from(data.row_id);
|
||||
manager
|
||||
.translate_row(data.view_id, row_id, data.field_id)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -91,6 +91,7 @@ pub fn init(database_manager: Weak<DatabaseManager>) -> AFPlugin {
|
||||
.event(DatabaseEvent::GetRelatedDatabaseRows, get_related_database_rows_handler)
|
||||
// AI
|
||||
.event(DatabaseEvent::SummarizeRow, summarize_row_handler)
|
||||
.event(DatabaseEvent::TranslateRow, translate_row_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)
|
||||
@ -373,4 +374,7 @@ pub enum DatabaseEvent {
|
||||
|
||||
#[event(input = "SummaryRowPB")]
|
||||
SummarizeRow = 174,
|
||||
|
||||
#[event(input = "TranslateRowPB")]
|
||||
TranslateRow = 175,
|
||||
}
|
||||
|
@ -17,15 +17,19 @@ use tracing::{event, instrument, trace};
|
||||
|
||||
use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabBuilderConfig};
|
||||
use collab_integrate::{CollabKVAction, CollabKVDB, CollabPersistenceConfig};
|
||||
use flowy_database_pub::cloud::{DatabaseCloudService, SummaryRowContent};
|
||||
use flowy_database_pub::cloud::{
|
||||
DatabaseCloudService, SummaryRowContent, TranslateItem, TranslateRowContent,
|
||||
};
|
||||
use flowy_error::{internal_error, FlowyError, FlowyResult};
|
||||
use lib_infra::box_any::BoxAny;
|
||||
use lib_infra::priority_task::TaskDispatcher;
|
||||
|
||||
use crate::entities::{DatabaseLayoutPB, DatabaseSnapshotPB};
|
||||
use crate::entities::{DatabaseLayoutPB, DatabaseSnapshotPB, FieldType};
|
||||
use crate::services::cell::stringify_cell;
|
||||
use crate::services::database::DatabaseEditor;
|
||||
use crate::services::database_view::DatabaseLayoutDepsResolver;
|
||||
use crate::services::field::translate_type_option::translate::TranslateTypeOption;
|
||||
|
||||
use crate::services::field_settings::default_field_settings_by_layout_map;
|
||||
use crate::services::share::csv::{CSVFormat, CSVImporter, ImportResult};
|
||||
|
||||
@ -459,6 +463,77 @@ impl DatabaseManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub async fn translate_row(
|
||||
&self,
|
||||
view_id: String,
|
||||
row_id: RowId,
|
||||
field_id: String,
|
||||
) -> FlowyResult<()> {
|
||||
let database = self.get_database_with_view_id(&view_id).await?;
|
||||
let mut translate_row_content = TranslateRowContent::new();
|
||||
let mut language = "english".to_string();
|
||||
|
||||
if let Some(row) = database.get_row(&view_id, &row_id) {
|
||||
let fields = database.get_fields(&view_id, None);
|
||||
for field in fields {
|
||||
// When translate a row, skip the content in the "AI Translate" cell; it does not need to
|
||||
// be translated.
|
||||
if field.id != field_id {
|
||||
if let Some(cell) = row.cells.get(&field.id) {
|
||||
translate_row_content.push(TranslateItem {
|
||||
title: field.name.clone(),
|
||||
content: stringify_cell(cell, &field),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
language = TranslateTypeOption::language_from_type(
|
||||
field
|
||||
.type_options
|
||||
.get(&FieldType::Translate.to_string())
|
||||
.cloned()
|
||||
.map(TranslateTypeOption::from)
|
||||
.unwrap_or_default()
|
||||
.language_type,
|
||||
)
|
||||
.to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call the cloud service to summarize the row.
|
||||
trace!(
|
||||
"[AI]:translate to {}, content:{:?}",
|
||||
language,
|
||||
translate_row_content
|
||||
);
|
||||
let response = self
|
||||
.cloud_service
|
||||
.translate_database_row(&self.user.workspace_id()?, translate_row_content, &language)
|
||||
.await?;
|
||||
|
||||
// Format the response items into a single string
|
||||
let content = response
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|value| {
|
||||
value
|
||||
.into_iter()
|
||||
.map(|(_k, v)| v.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(",");
|
||||
|
||||
trace!("[AI]:translate row response: {}", content);
|
||||
// Update the cell with the response from the cloud service.
|
||||
database
|
||||
.update_cell_with_changeset(&view_id, &row_id, &field_id, BoxAny::new(content))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Only expose this method for testing
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn get_cloud_service(&self) -> &Arc<dyn DatabaseCloudService> {
|
||||
|
@ -262,6 +262,9 @@ impl<'a> CellBuilder<'a> {
|
||||
FieldType::Summary => {
|
||||
cells.insert(field_id, insert_text_cell(cell_str, field));
|
||||
},
|
||||
FieldType::Translate => {
|
||||
cells.insert(field_id, insert_text_cell(cell_str, field));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1703,8 +1703,9 @@ pub async fn update_field_type_option_fn(
|
||||
update.update_type_options(|type_options_update| {
|
||||
event!(
|
||||
tracing::Level::TRACE,
|
||||
"insert type option to field type: {:?}",
|
||||
field_type
|
||||
"insert type option to field type: {:?}, {:?}",
|
||||
field_type,
|
||||
type_option_data
|
||||
);
|
||||
type_options_update.insert(&field_type.to_string(), type_option_data);
|
||||
});
|
||||
|
@ -7,6 +7,7 @@ pub mod selection_type_option;
|
||||
pub mod summary_type_option;
|
||||
pub mod text_type_option;
|
||||
pub mod timestamp_type_option;
|
||||
pub mod translate_type_option;
|
||||
mod type_option;
|
||||
mod type_option_cell;
|
||||
mod url_type_option;
|
||||
|
@ -85,6 +85,7 @@ impl CellDataDecoder for RichTextTypeOption {
|
||||
| FieldType::CreatedTime
|
||||
| FieldType::Relation => None,
|
||||
FieldType::Summary => Some(StringCellData::from(stringify_cell(cell, field))),
|
||||
FieldType::Translate => Some(StringCellData::from(stringify_cell(cell, field))),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,2 @@
|
||||
pub mod translate;
|
||||
pub mod translate_entities;
|
@ -0,0 +1,137 @@
|
||||
use crate::entities::TextFilterPB;
|
||||
use crate::services::cell::{CellDataChangeset, CellDataDecoder};
|
||||
use crate::services::field::type_options::translate_type_option::translate_entities::TranslateCellData;
|
||||
use crate::services::field::type_options::util::ProtobufStr;
|
||||
use crate::services::field::{
|
||||
TypeOption, TypeOptionCellData, TypeOptionCellDataCompare, TypeOptionCellDataFilter,
|
||||
TypeOptionCellDataSerde, TypeOptionTransform,
|
||||
};
|
||||
use crate::services::sort::SortCondition;
|
||||
use collab::core::any_map::AnyMapExtension;
|
||||
use collab_database::fields::{TypeOptionData, TypeOptionDataBuilder};
|
||||
use collab_database::rows::Cell;
|
||||
use flowy_error::FlowyResult;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TranslateTypeOption {
|
||||
pub auto_fill: bool,
|
||||
/// Use [TranslateTypeOption::language_from_type] to get the language name
|
||||
pub language_type: i64,
|
||||
}
|
||||
|
||||
impl TranslateTypeOption {
|
||||
pub fn language_from_type(language_type: i64) -> &'static str {
|
||||
match language_type {
|
||||
0 => "Chinese",
|
||||
1 => "English",
|
||||
2 => "French",
|
||||
3 => "German",
|
||||
_ => "English",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TranslateTypeOption {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
auto_fill: false,
|
||||
language_type: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TypeOptionData> for TranslateTypeOption {
|
||||
fn from(value: TypeOptionData) -> Self {
|
||||
let auto_fill = value.get_bool_value("auto_fill").unwrap_or_default();
|
||||
let language = value.get_i64_value("language").unwrap_or_default();
|
||||
Self {
|
||||
auto_fill,
|
||||
language_type: language,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TranslateTypeOption> for TypeOptionData {
|
||||
fn from(value: TranslateTypeOption) -> Self {
|
||||
TypeOptionDataBuilder::new()
|
||||
.insert_bool_value("auto_fill", value.auto_fill)
|
||||
.insert_i64_value("language", value.language_type)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeOption for TranslateTypeOption {
|
||||
type CellData = TranslateCellData;
|
||||
type CellChangeset = String;
|
||||
type CellProtobufType = ProtobufStr;
|
||||
type CellFilter = TextFilterPB;
|
||||
}
|
||||
|
||||
impl CellDataChangeset for TranslateTypeOption {
|
||||
fn apply_changeset(
|
||||
&self,
|
||||
changeset: String,
|
||||
_cell: Option<Cell>,
|
||||
) -> FlowyResult<(Cell, TranslateCellData)> {
|
||||
let cell_data = TranslateCellData(changeset);
|
||||
Ok((cell_data.clone().into(), cell_data))
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeOptionCellDataFilter for TranslateTypeOption {
|
||||
fn apply_filter(
|
||||
&self,
|
||||
filter: &<Self as TypeOption>::CellFilter,
|
||||
cell_data: &<Self as TypeOption>::CellData,
|
||||
) -> bool {
|
||||
filter.is_visible(cell_data)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeOptionCellDataCompare for TranslateTypeOption {
|
||||
fn apply_cmp(
|
||||
&self,
|
||||
cell_data: &<Self as TypeOption>::CellData,
|
||||
other_cell_data: &<Self as TypeOption>::CellData,
|
||||
sort_condition: SortCondition,
|
||||
) -> Ordering {
|
||||
match (cell_data.is_cell_empty(), other_cell_data.is_cell_empty()) {
|
||||
(true, true) => Ordering::Equal,
|
||||
(true, false) => Ordering::Greater,
|
||||
(false, true) => Ordering::Less,
|
||||
(false, false) => {
|
||||
let order = cell_data.0.cmp(&other_cell_data.0);
|
||||
sort_condition.evaluate_order(order)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CellDataDecoder for TranslateTypeOption {
|
||||
fn decode_cell(&self, cell: &Cell) -> FlowyResult<TranslateCellData> {
|
||||
Ok(TranslateCellData::from(cell))
|
||||
}
|
||||
|
||||
fn stringify_cell_data(&self, cell_data: TranslateCellData) -> String {
|
||||
cell_data.to_string()
|
||||
}
|
||||
|
||||
fn numeric_cell(&self, _cell: &Cell) -> Option<f64> {
|
||||
None
|
||||
}
|
||||
}
|
||||
impl TypeOptionTransform for TranslateTypeOption {}
|
||||
|
||||
impl TypeOptionCellDataSerde for TranslateTypeOption {
|
||||
fn protobuf_encode(
|
||||
&self,
|
||||
cell_data: <Self as TypeOption>::CellData,
|
||||
) -> <Self as TypeOption>::CellProtobufType {
|
||||
ProtobufStr::from(cell_data.0)
|
||||
}
|
||||
|
||||
fn parse_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
|
||||
Ok(TranslateCellData::from(cell))
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
use crate::entities::FieldType;
|
||||
use crate::services::field::{TypeOptionCellData, CELL_DATA};
|
||||
use collab::core::any_map::AnyMapExtension;
|
||||
use collab_database::rows::{new_cell_builder, Cell};
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct TranslateCellData(pub String);
|
||||
impl std::ops::Deref for TranslateCellData {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeOptionCellData for TranslateCellData {
|
||||
fn is_cell_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Cell> for TranslateCellData {
|
||||
fn from(cell: &Cell) -> Self {
|
||||
Self(cell.get_str_value(CELL_DATA).unwrap_or_default())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TranslateCellData> for Cell {
|
||||
fn from(data: TranslateCellData) -> Self {
|
||||
new_cell_builder(FieldType::Translate)
|
||||
.insert_str_value(CELL_DATA, data.0)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for TranslateCellData {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for TranslateCellData {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
@ -11,11 +11,13 @@ use flowy_error::FlowyResult;
|
||||
use crate::entities::{
|
||||
CheckboxTypeOptionPB, ChecklistTypeOptionPB, DateTypeOptionPB, FieldType,
|
||||
MultiSelectTypeOptionPB, NumberTypeOptionPB, RelationTypeOptionPB, RichTextTypeOptionPB,
|
||||
SingleSelectTypeOptionPB, SummarizationTypeOptionPB, TimestampTypeOptionPB, URLTypeOptionPB,
|
||||
SingleSelectTypeOptionPB, SummarizationTypeOptionPB, TimestampTypeOptionPB,
|
||||
TranslateTypeOptionPB, URLTypeOptionPB,
|
||||
};
|
||||
use crate::services::cell::CellDataDecoder;
|
||||
use crate::services::field::checklist_type_option::ChecklistTypeOption;
|
||||
use crate::services::field::summary_type_option::summary::SummarizationTypeOption;
|
||||
use crate::services::field::translate_type_option::translate::TranslateTypeOption;
|
||||
use crate::services::field::{
|
||||
CheckboxTypeOption, DateTypeOption, MultiSelectTypeOption, NumberTypeOption, RelationTypeOption,
|
||||
RichTextTypeOption, SingleSelectTypeOption, TimestampTypeOption, URLTypeOption,
|
||||
@ -185,6 +187,9 @@ pub fn type_option_data_from_pb<T: Into<Bytes>>(
|
||||
FieldType::Summary => {
|
||||
SummarizationTypeOptionPB::try_from(bytes).map(|pb| SummarizationTypeOption::from(pb).into())
|
||||
},
|
||||
FieldType::Translate => {
|
||||
TranslateTypeOptionPB::try_from(bytes).map(|pb| TranslateTypeOption::from(pb).into())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,6 +257,12 @@ pub fn type_option_to_pb(type_option: TypeOptionData, field_type: &FieldType) ->
|
||||
.try_into()
|
||||
.unwrap()
|
||||
},
|
||||
FieldType::Translate => {
|
||||
let translate_type_option: TranslateTypeOption = type_option.into();
|
||||
TranslateTypeOptionPB::from(translate_type_option)
|
||||
.try_into()
|
||||
.unwrap()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,5 +283,6 @@ pub fn default_type_option_data_from_type(field_type: FieldType) -> TypeOptionDa
|
||||
FieldType::Checklist => ChecklistTypeOption.into(),
|
||||
FieldType::Relation => RelationTypeOption::default().into(),
|
||||
FieldType::Summary => SummarizationTypeOption::default().into(),
|
||||
FieldType::Translate => TranslateTypeOption::default().into(),
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ use lib_infra::box_any::BoxAny;
|
||||
use crate::entities::FieldType;
|
||||
use crate::services::cell::{CellCache, CellDataChangeset, CellDataDecoder, CellProtobufBlob};
|
||||
use crate::services::field::summary_type_option::summary::SummarizationTypeOption;
|
||||
use crate::services::field::translate_type_option::translate::TranslateTypeOption;
|
||||
use crate::services::field::{
|
||||
CheckboxTypeOption, ChecklistTypeOption, DateTypeOption, MultiSelectTypeOption, NumberTypeOption,
|
||||
RelationTypeOption, RichTextTypeOption, SingleSelectTypeOption, TimestampTypeOption, TypeOption,
|
||||
@ -449,6 +450,16 @@ impl<'a> TypeOptionCellExt<'a> {
|
||||
self.cell_data_cache.clone(),
|
||||
)
|
||||
}),
|
||||
FieldType::Translate => self
|
||||
.field
|
||||
.get_type_option::<TranslateTypeOption>(field_type)
|
||||
.map(|type_option| {
|
||||
TypeOptionCellDataHandlerImpl::new_with_boxed(
|
||||
type_option,
|
||||
field_type,
|
||||
self.cell_data_cache.clone(),
|
||||
)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -552,6 +563,9 @@ fn get_type_option_transform_handler(
|
||||
},
|
||||
FieldType::Summary => Box::new(SummarizationTypeOption::from(type_option_data))
|
||||
as Box<dyn TypeOptionTransformHandler>,
|
||||
FieldType::Translate => {
|
||||
Box::new(TranslateTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,6 +281,7 @@ impl FilterInner {
|
||||
FieldType::Checkbox => BoxAny::new(CheckboxFilterPB::parse(condition as u8, content)),
|
||||
FieldType::Relation => BoxAny::new(RelationFilterPB::parse(condition as u8, content)),
|
||||
FieldType::Summary => BoxAny::new(TextFilterPB::parse(condition as u8, content)),
|
||||
FieldType::Translate => BoxAny::new(TextFilterPB::parse(condition as u8, content)),
|
||||
};
|
||||
|
||||
FilterInner::Data {
|
||||
@ -367,6 +368,10 @@ impl<'a> From<&'a Filter> for FilterMap {
|
||||
let filter = condition_and_content.cloned::<TextFilterPB>()?;
|
||||
(filter.condition as u8, filter.content)
|
||||
},
|
||||
FieldType::Translate => {
|
||||
let filter = condition_and_content.cloned::<TextFilterPB>()?;
|
||||
(filter.condition as u8, filter.content)
|
||||
},
|
||||
};
|
||||
Some((condition, content))
|
||||
};
|
||||
|
Reference in New Issue
Block a user