Skip to content

Commit

Permalink
feat: add default type option data from type
Browse files Browse the repository at this point in the history
  • Loading branch information
khorshuheng committed Nov 22, 2024
1 parent 699d6e3 commit 54e459f
Show file tree
Hide file tree
Showing 9 changed files with 387 additions and 2 deletions.
154 changes: 152 additions & 2 deletions collab-database/src/entity.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
#![allow(clippy::upper_case_acronyms)]
use crate::database::{gen_database_id, gen_database_view_id, gen_row_id, timestamp, DatabaseData};
use crate::error::DatabaseError;
use crate::fields::Field;
use crate::fields::checkbox_type_option::CheckboxTypeOption;
use crate::fields::checklist_type_option::ChecklistTypeOption;
use crate::fields::date_type_option::{DateTypeOption, TimeTypeOption};
use crate::fields::media_type_option::MediaTypeOption;
use crate::fields::number_type_option::NumberTypeOption;
use crate::fields::relation_type_option::RelationTypeOption;
use crate::fields::select_type_option::{MultiSelectTypeOption, SingleSelectTypeOption};
use crate::fields::summary_type_option::SummarizationTypeOption;
use crate::fields::text_type_option::RichTextTypeOption;
use crate::fields::timestamp_type_option::TimestampTypeOption;
use crate::fields::translate_type_option::TranslateTypeOption;
use crate::fields::url_type_option::URLTypeOption;
use crate::fields::{Field, TypeOptionData};
use crate::rows::CreateRowParams;
use crate::views::{
DatabaseLayout, FieldOrder, FieldSettingsByFieldIdMap, FieldSettingsMap, FilterMap,
Expand All @@ -13,6 +25,7 @@ use collab_entity::CollabType;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
use tracing::error;
use yrs::{Any, Out};

Expand Down Expand Up @@ -254,7 +267,7 @@ impl CreateDatabaseParams {
}
}

#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize_repr, Deserialize_repr)]
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum FieldType {
RichText = 0,
Expand Down Expand Up @@ -297,6 +310,120 @@ impl TryFrom<yrs::Out> for FieldType {
}
}

impl Display for FieldType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let value: i64 = (*self).into();
f.write_fmt(format_args!("{}", value))
}
}

impl AsRef<FieldType> for FieldType {
fn as_ref(&self) -> &FieldType {
self
}
}

impl From<&FieldType> for FieldType {
fn from(field_type: &FieldType) -> Self {
*field_type
}
}

impl FieldType {
pub fn value(&self) -> i64 {
(*self).into()
}

pub fn default_name(&self) -> String {
let s = match self {
FieldType::RichText => "Text",
FieldType::Number => "Number",
FieldType::DateTime => "Date",
FieldType::SingleSelect => "Single Select",
FieldType::MultiSelect => "Multi Select",
FieldType::Checkbox => "Checkbox",
FieldType::URL => "URL",
FieldType::Checklist => "Checklist",
FieldType::LastEditedTime => "Last modified",
FieldType::CreatedTime => "Created time",
FieldType::Relation => "Relation",
FieldType::Summary => "Summarize",
FieldType::Translate => "Translate",
FieldType::Time => "Time",
FieldType::Media => "Media",
};
s.to_string()
}

pub fn is_ai_field(&self) -> bool {
matches!(self, FieldType::Summary | FieldType::Translate)
}

pub fn is_number(&self) -> bool {
matches!(self, FieldType::Number)
}

pub fn is_text(&self) -> bool {
matches!(self, FieldType::RichText)
}

pub fn is_checkbox(&self) -> bool {
matches!(self, FieldType::Checkbox)
}

pub fn is_date(&self) -> bool {
matches!(self, FieldType::DateTime)
}

pub fn is_single_select(&self) -> bool {
matches!(self, FieldType::SingleSelect)
}

pub fn is_multi_select(&self) -> bool {
matches!(self, FieldType::MultiSelect)
}

pub fn is_last_edited_time(&self) -> bool {
matches!(self, FieldType::LastEditedTime)
}

pub fn is_created_time(&self) -> bool {
matches!(self, FieldType::CreatedTime)
}

pub fn is_url(&self) -> bool {
matches!(self, FieldType::URL)
}

pub fn is_select_option(&self) -> bool {
self.is_single_select() || self.is_multi_select()
}

pub fn is_checklist(&self) -> bool {
matches!(self, FieldType::Checklist)
}

pub fn is_relation(&self) -> bool {
matches!(self, FieldType::Relation)
}

pub fn is_time(&self) -> bool {
matches!(self, FieldType::Time)
}

pub fn is_media(&self) -> bool {
matches!(self, FieldType::Media)
}

pub fn can_be_group(&self) -> bool {
self.is_select_option() || self.is_checkbox() || self.is_url()
}

pub fn is_auto_update(&self) -> bool {
self.is_last_edited_time()
}
}

impl From<i64> for FieldType {
fn from(index: i64) -> Self {
match index {
Expand All @@ -323,6 +450,29 @@ impl From<i64> for FieldType {
}
}

pub fn default_type_option_data_from_type(field_type: FieldType) -> TypeOptionData {
match field_type {
FieldType::RichText => RichTextTypeOption.into(),
FieldType::Number => NumberTypeOption::default().into(),
FieldType::DateTime => DateTypeOption::default().into(),
FieldType::LastEditedTime | FieldType::CreatedTime => TimestampTypeOption {
field_type: field_type.into(),
..Default::default()
}
.into(),
FieldType::SingleSelect => SingleSelectTypeOption::default().into(),
FieldType::MultiSelect => MultiSelectTypeOption::default().into(),
FieldType::Checkbox => CheckboxTypeOption.into(),
FieldType::URL => URLTypeOption::default().into(),
FieldType::Time => TimeTypeOption.into(),
FieldType::Media => MediaTypeOption::default().into(),
FieldType::Checklist => ChecklistTypeOption.into(),
FieldType::Relation => RelationTypeOption::default().into(),
FieldType::Summary => SummarizationTypeOption::default().into(),
FieldType::Translate => TranslateTypeOption::default().into(),
}
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum FileUploadType {
Expand Down
13 changes: 13 additions & 0 deletions collab-database/src/fields/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use serde::{Deserialize, Serialize};

use collab::preclude::{Any, Map, MapExt, MapRef, ReadTxn, TransactionMut, YrsValue};

use crate::database::gen_field_id;
use crate::entity::{default_type_option_data_from_type, FieldType};
use crate::fields::{TypeOptionData, TypeOptions, TypeOptionsUpdate};
use crate::{impl_bool_update, impl_i64_update, impl_str_update};

Expand Down Expand Up @@ -37,6 +39,17 @@ impl Field {
self
}

pub fn from_field_type(name: &str, field_type: FieldType, is_primary: bool) -> Self {
let new_field = Self {
id: gen_field_id(),
name: name.to_string(),
field_type: field_type.into(),
is_primary,
..Default::default()
};
new_field.with_type_option_data(field_type, default_type_option_data_from_type(field_type))
}

pub fn get_type_option<T: From<TypeOptionData>>(&self, type_id: impl ToString) -> Option<T> {
let type_option_data = self.type_options.get(&type_id.to_string())?.clone();
Some(T::from(type_option_data))
Expand Down
100 changes: 100 additions & 0 deletions collab-database/src/fields/field_settings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use collab::util::AnyMapExt;
use yrs::Any;

use crate::views::{DatabaseLayout, FieldSettingsMap, FieldSettingsMapBuilder};

#[repr(u8)]
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub enum FieldVisibility {
#[default]
AlwaysShown = 0,
HideWhenEmpty = 1,
AlwaysHidden = 2,
}

macro_rules! impl_into_field_visibility {
($target: ident) => {
impl std::convert::From<$target> for FieldVisibility {
fn from(ty: $target) -> Self {
match ty {
0 => FieldVisibility::AlwaysShown,
1 => FieldVisibility::HideWhenEmpty,
2 => FieldVisibility::AlwaysHidden,
_ => {
tracing::error!("🔴Can't parse FieldVisibility from value: {}", ty);
FieldVisibility::AlwaysShown
},
}
}
}
};
}

impl_into_field_visibility!(i64);
impl_into_field_visibility!(u8);

impl From<FieldVisibility> for i64 {
fn from(value: FieldVisibility) -> Self {
(value as u8) as i64
}
}

/// Stores the field settings for a single field
#[derive(Debug, Clone)]
pub struct FieldSettings {
pub field_id: String,
pub visibility: FieldVisibility,
pub width: i32,
pub wrap_cell_content: bool,
}

pub const VISIBILITY: &str = "visibility";
pub const WIDTH: &str = "width";
pub const DEFAULT_WIDTH: i32 = 150;
pub const WRAP_CELL_CONTENT: &str = "wrap";

pub fn default_field_visibility(layout_type: DatabaseLayout) -> FieldVisibility {
match layout_type {
DatabaseLayout::Grid => FieldVisibility::AlwaysShown,
DatabaseLayout::Board => FieldVisibility::HideWhenEmpty,
DatabaseLayout::Calendar => FieldVisibility::HideWhenEmpty,
}
}

impl FieldSettings {
pub fn from_any_map(
field_id: &str,
layout_type: DatabaseLayout,
field_settings: &FieldSettingsMap,
) -> Self {
let visibility = field_settings
.get_as::<i64>(VISIBILITY)
.map(Into::into)
.unwrap_or_else(|| default_field_visibility(layout_type));
let width = field_settings.get_as::<i32>(WIDTH).unwrap_or(DEFAULT_WIDTH);
let wrap_cell_content: bool = field_settings.get_as(WRAP_CELL_CONTENT).unwrap_or(true);

Self {
field_id: field_id.to_string(),
visibility,
width,
wrap_cell_content,
}
}
}

impl From<FieldSettings> for FieldSettingsMap {
fn from(field_settings: FieldSettings) -> Self {
FieldSettingsMapBuilder::from([
(
VISIBILITY.into(),
Any::BigInt(i64::from(field_settings.visibility)),
),
(WIDTH.into(), Any::BigInt(field_settings.width as i64)),
(
WRAP_CELL_CONTENT.into(),
Any::Bool(field_settings.wrap_cell_content),
),
])
}
}
2 changes: 2 additions & 0 deletions collab-database/src/fields/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ mod field;
mod field_id;
mod field_map;
mod field_observer;
mod field_settings;
mod type_option;

pub use field::*;
pub use field_id::*;
pub use field_map::*;
pub use field_observer::*;
pub use field_settings::*;
pub use type_option::*;
18 changes: 18 additions & 0 deletions collab-database/src/fields/type_option/checklist_type_option.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use serde::{Deserialize, Serialize};

use super::{TypeOptionData, TypeOptionDataBuilder};

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ChecklistTypeOption;

impl From<TypeOptionData> for ChecklistTypeOption {
fn from(_data: TypeOptionData) -> Self {
Self
}
}

impl From<ChecklistTypeOption> for TypeOptionData {
fn from(_data: ChecklistTypeOption) -> Self {
TypeOptionDataBuilder::default()
}
}
4 changes: 4 additions & 0 deletions collab-database/src/fields/type_option/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
pub mod checkbox_type_option;
pub mod checklist_type_option;
pub mod date_type_option;
pub mod media_type_option;
pub mod number_type_option;
pub mod relation_type_option;
pub mod select_type_option;
pub mod summary_type_option;
pub mod text_type_option;
pub mod timestamp_type_option;
pub mod translate_type_option;
pub mod url_type_option;

use std::collections::HashMap;
Expand Down
22 changes: 22 additions & 0 deletions collab-database/src/fields/type_option/relation_type_option.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use collab::util::AnyMapExt;
use serde::{Deserialize, Serialize};

use super::{TypeOptionData, TypeOptionDataBuilder};

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct RelationTypeOption {
pub database_id: String,
}

impl From<TypeOptionData> for RelationTypeOption {
fn from(data: TypeOptionData) -> Self {
let database_id: String = data.get_as("database_id").unwrap_or_default();
Self { database_id }
}
}

impl From<RelationTypeOption> for TypeOptionData {
fn from(data: RelationTypeOption) -> Self {
TypeOptionDataBuilder::from([("database_id".into(), data.database_id.into())])
}
}
Loading

0 comments on commit 54e459f

Please sign in to comment.