fix: number sort (#2570)

* chore: remove sign

* fix: sort number

* chore: update patch

* ci: fix dart test

* chore: fmt
This commit is contained in:
Nathan.fooo 2023-05-21 11:13:22 +08:00 committed by GitHub
parent bb681bdd1e
commit 6c31cf9555
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 291 additions and 689 deletions

View File

@ -151,7 +151,6 @@ class RowList {
(rowInfo) => rowInfo.rowPB.id == rowId,
);
if (index != -1) {
assert(index == oldIndex);
final rowInfo = remove(rowId)!.rowInfo;
insert(newIndex, rowInfo);
}

View File

@ -34,12 +34,12 @@ default = ["custom-protocol"]
custom-protocol = ["tauri/custom-protocol"]
[patch.crates-io]
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d074c9" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d074c9" }
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d074c9" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d074c9" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d074c9" }
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d074c9" }
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
#collab = { path = "../../AppFlowy-Collab/collab" }
#collab-folder = { path = "../../AppFlowy-Collab/collab-folder" }

View File

@ -85,7 +85,7 @@ checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
[[package]]
name = "appflowy-integrate"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d074c9#d074c94471f222e2701fe451f13c51aab39d2bf8"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
dependencies = [
"collab",
"collab-database",
@ -574,9 +574,9 @@ dependencies = [
[[package]]
name = "bindgen"
version = "0.64.0"
version = "0.65.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4"
checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5"
dependencies = [
"bitflags",
"cexpr",
@ -584,12 +584,13 @@ dependencies = [
"lazy_static",
"lazycell",
"peeking_take_while",
"prettyplease",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn 1.0.109",
"syn 2.0.15",
]
[[package]]
@ -883,7 +884,7 @@ dependencies = [
[[package]]
name = "collab"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d074c9#d074c94471f222e2701fe451f13c51aab39d2bf8"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
dependencies = [
"anyhow",
"bytes",
@ -900,7 +901,7 @@ dependencies = [
[[package]]
name = "collab-client-ws"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d074c9#d074c94471f222e2701fe451f13c51aab39d2bf8"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
dependencies = [
"bytes",
"collab-sync",
@ -918,7 +919,7 @@ dependencies = [
[[package]]
name = "collab-database"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d074c9#d074c94471f222e2701fe451f13c51aab39d2bf8"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
dependencies = [
"anyhow",
"async-trait",
@ -942,7 +943,7 @@ dependencies = [
[[package]]
name = "collab-derive"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d074c9#d074c94471f222e2701fe451f13c51aab39d2bf8"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
dependencies = [
"proc-macro2",
"quote",
@ -954,7 +955,7 @@ dependencies = [
[[package]]
name = "collab-document"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d074c9#d074c94471f222e2701fe451f13c51aab39d2bf8"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
dependencies = [
"anyhow",
"collab",
@ -971,7 +972,7 @@ dependencies = [
[[package]]
name = "collab-folder"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d074c9#d074c94471f222e2701fe451f13c51aab39d2bf8"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
dependencies = [
"anyhow",
"collab",
@ -989,7 +990,7 @@ dependencies = [
[[package]]
name = "collab-persistence"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d074c9#d074c94471f222e2701fe451f13c51aab39d2bf8"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
dependencies = [
"bincode",
"chrono",
@ -1009,7 +1010,7 @@ dependencies = [
[[package]]
name = "collab-plugins"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d074c9#d074c94471f222e2701fe451f13c51aab39d2bf8"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
dependencies = [
"anyhow",
"async-trait",
@ -1035,7 +1036,7 @@ dependencies = [
[[package]]
name = "collab-sync"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d074c9#d074c94471f222e2701fe451f13c51aab39d2bf8"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
dependencies = [
"bytes",
"collab",
@ -1817,6 +1818,7 @@ dependencies = [
"serde",
"serde_json",
"serial_test",
"tempdir",
"thread-id",
"tokio",
]
@ -1899,6 +1901,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "futures"
version = "0.3.28"
@ -2584,9 +2592,9 @@ checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
[[package]]
name = "librocksdb-sys"
version = "0.10.0+7.9.2"
version = "0.11.0+8.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fe4d5874f5ff2bc616e55e8c6086d478fcda13faf9495768a4aa1c22042d30b"
checksum = "d3386f101bcb4bd252d8e9d2fb41ec3b0862a15a62b478c355b2982efa469e3e"
dependencies = [
"bindgen",
"bzip2-sys",
@ -3235,6 +3243,16 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "prettyplease"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ceca8aaf45b5c46ec7ed39fff75f57290368c1846d33d24a122ca81416ab058"
dependencies = [
"proc-macro2",
"syn 2.0.15",
]
[[package]]
name = "proc-macro-crate"
version = "0.1.5"
@ -3475,6 +3493,19 @@ dependencies = [
"scheduled-thread-pool",
]
[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
dependencies = [
"fuchsia-cprng",
"libc",
"rand_core 0.3.1",
"rdrand",
"winapi",
]
[[package]]
name = "rand"
version = "0.7.3"
@ -3520,6 +3551,21 @@ dependencies = [
"rand_core 0.6.4",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
"rand_core 0.4.2",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rand_core"
version = "0.5.1"
@ -3578,6 +3624,15 @@ dependencies = [
"num_cpus",
]
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "redox_syscall"
version = "0.1.57"
@ -3639,6 +3694,15 @@ version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
name = "rend"
version = "0.4.0"
@ -3727,9 +3791,9 @@ dependencies = [
[[package]]
name = "rocksdb"
version = "0.20.1"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "015439787fce1e75d55f279078d33ff14b4af5d93d995e8838ee4631301c8a99"
checksum = "bb6f170a4041d50a0ce04b0d2e14916d6ca863ea2e422689a5b694395d299ffe"
dependencies = [
"libc",
"librocksdb-sys",
@ -4242,6 +4306,16 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "tempdir"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
dependencies = [
"rand 0.4.6",
"remove_dir_all",
]
[[package]]
name = "tempfile"
version = "3.5.0"

View File

@ -32,11 +32,11 @@ opt-level = 3
incremental = false
[patch.crates-io]
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d074c9" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d074c9" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d074c9" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d074c9" }
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d074c9" }
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
#collab = { path = "../AppFlowy-Collab/collab" }
#collab-folder = { path = "../AppFlowy-Collab/collab-folder" }

View File

@ -84,7 +84,7 @@ fn create_log_filter(level: String, with_crates: Vec<String>) -> String {
filters.push(format!("flowy_core={}", level));
filters.push(format!("flowy_folder2={}", level));
filters.push(format!("collab_folder={}", level));
// filters.push(format!("collab_persistence={}", level));
filters.push(format!("collab_persistence={}", level));
filters.push(format!("collab_database={}", level));
filters.push(format!("collab_plugins={}", level));
filters.push(format!("appflowy_integrate={}", level));

View File

@ -13,9 +13,6 @@ pub struct NumberTypeOptionPB {
#[pb(index = 3)]
pub symbol: String,
#[pb(index = 4)]
pub sign_positive: bool,
#[pb(index = 5)]
pub name: String,
}
@ -26,7 +23,6 @@ impl From<NumberTypeOption> for NumberTypeOptionPB {
format: data.format.into(),
scale: data.scale,
symbol: data.symbol,
sign_positive: data.sign_positive,
name: data.name,
}
}
@ -38,7 +34,6 @@ impl From<NumberTypeOptionPB> for NumberTypeOption {
format: data.format.into(),
scale: data.scale,
symbol: data.symbol,
sign_positive: data.sign_positive,
name: data.name,
}
}

View File

@ -347,7 +347,7 @@ pub(crate) async fn get_cell_handler(
data_result_ok(cell)
}
#[tracing::instrument(level = "trace", skip_all, err)]
#[tracing::instrument(level = "debug", skip_all, err)]
pub(crate) async fn update_cell_handler(
data: AFPluginData<CellChangesetPB>,
manager: AFPluginState<Arc<DatabaseManager2>>,

View File

@ -58,7 +58,7 @@ pub trait CellDataChangeset: TypeOption {
/// FieldType::SingleSelect => SelectOptionChangeset
///
/// cell_rev: It will be None if the cell does not contain any data.
pub fn apply_cell_data_changeset<C: ToCellChangeset>(
pub fn apply_cell_changeset<C: ToCellChangeset>(
changeset: C,
cell: Option<Cell>,
field: &Field,
@ -194,11 +194,11 @@ pub fn stringify_cell_data(
}
pub fn insert_text_cell(s: String, field: &Field) -> Cell {
apply_cell_data_changeset(s, None, field, None).unwrap()
apply_cell_changeset(s, None, field, None).unwrap()
}
pub fn insert_number_cell(num: i64, field: &Field) -> Cell {
apply_cell_data_changeset(num.to_string(), None, field, None).unwrap()
apply_cell_changeset(num.to_string(), None, field, None).unwrap()
}
pub fn insert_url_cell(url: String, field: &Field) -> Cell {
@ -212,7 +212,7 @@ pub fn insert_url_cell(url: String, field: &Field) -> Cell {
_ => url,
};
apply_cell_data_changeset(url, None, field, None).unwrap()
apply_cell_changeset(url, None, field, None).unwrap()
}
pub fn insert_checkbox_cell(is_check: bool, field: &Field) -> Cell {
@ -221,7 +221,7 @@ pub fn insert_checkbox_cell(is_check: bool, field: &Field) -> Cell {
} else {
UNCHECK.to_string()
};
apply_cell_data_changeset(s, None, field, None).unwrap()
apply_cell_changeset(s, None, field, None).unwrap()
}
pub fn insert_date_cell(timestamp: i64, field: &Field) -> Cell {
@ -232,19 +232,19 @@ pub fn insert_date_cell(timestamp: i64, field: &Field) -> Cell {
timezone_id: None,
})
.unwrap();
apply_cell_data_changeset(cell_data, None, field, None).unwrap()
apply_cell_changeset(cell_data, None, field, None).unwrap()
}
pub fn insert_select_option_cell(option_ids: Vec<String>, field: &Field) -> Cell {
let changeset =
SelectOptionCellChangeset::from_insert_options(option_ids).to_cell_changeset_str();
apply_cell_data_changeset(changeset, None, field, None).unwrap()
apply_cell_changeset(changeset, None, field, None).unwrap()
}
pub fn delete_select_option_cell(option_ids: Vec<String>, field: &Field) -> Cell {
let changeset =
SelectOptionCellChangeset::from_delete_options(option_ids).to_cell_changeset_str();
apply_cell_data_changeset(changeset, None, field, None).unwrap()
apply_cell_changeset(changeset, None, field, None).unwrap()
}
/// Deserialize the String into cell specific data type.

View File

@ -6,7 +6,7 @@ use bytes::Bytes;
use collab_database::database::{gen_row_id, timestamp, Database as InnerDatabase};
use collab_database::fields::{Field, TypeOptionData};
use collab_database::rows::{Cell, Cells, Row, RowCell, RowId};
use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting, RowOrder};
use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting};
use parking_lot::Mutex;
use tokio::sync::{broadcast, RwLock};
@ -23,14 +23,11 @@ use crate::entities::{
};
use crate::notification::{send_notification, DatabaseNotification};
use crate::services::cell::{
apply_cell_data_changeset, get_type_cell_protobuf, AnyTypeCache, CellBuilder, CellCache,
apply_cell_changeset, get_type_cell_protobuf, AnyTypeCache, CellBuilder, CellCache,
ToCellChangeset,
};
use crate::services::database::util::database_view_setting_pb_from_view;
use crate::services::database::{DatabaseRowEvent, InsertedRow, UpdatedRow};
use crate::services::database_view::{
DatabaseViewChanged, DatabaseViewData, DatabaseViews, RowEventSender,
};
use crate::services::database_view::{DatabaseViewChanged, DatabaseViewData, DatabaseViews};
use crate::services::field::{
default_type_option_data_for_type, default_type_option_data_from_type,
select_type_option_from_field, transform_type_option, type_option_data_from_pb_or_default,
@ -46,7 +43,6 @@ pub struct DatabaseEditor {
database: MutexDatabase,
pub cell_cache: CellCache,
database_views: Arc<DatabaseViews>,
row_event_tx: RowEventSender,
}
impl DatabaseEditor {
@ -55,27 +51,18 @@ impl DatabaseEditor {
task_scheduler: Arc<RwLock<TaskDispatcher>>,
) -> FlowyResult<Self> {
let cell_cache = AnyTypeCache::<u64>::new();
let (row_event_tx, row_event_rx) = broadcast::channel(100);
let database_view_data = Arc::new(DatabaseViewDataImpl {
database: database.clone(),
task_scheduler: task_scheduler.clone(),
cell_cache: cell_cache.clone(),
});
let database_views = Arc::new(
DatabaseViews::new(
database.clone(),
cell_cache.clone(),
database_view_data,
row_event_rx,
)
.await?,
);
let database_views =
Arc::new(DatabaseViews::new(database.clone(), cell_cache.clone(), database_view_data).await?);
Ok(Self {
database,
cell_cache,
database_views,
row_event_tx,
})
}
@ -295,7 +282,14 @@ impl DatabaseEditor {
// self.database.lock().views.update_view(view_id, |view| {
// view.move_row_order(from as u32, to as u32);
// });
// self.row_event_tx.send(DatabaseRowEvent::Move { from: _from, to: _to})
// let changeset = RowsChangesetPB::from_move(
// view_id.to_string(),
// vec![from.into_inner()],
// vec![to.into()],
// );
// send_notification(&self.view_id, DatabaseNotification::DidUpdateViewRows)
// .payload(changeset)
// .send();
}
pub async fn create_row(&self, params: CreateRowParams) -> FlowyResult<Option<Row>> {
@ -319,14 +313,6 @@ impl DatabaseEditor {
);
if let Some((index, row_order)) = result {
let _ = self
.row_event_tx
.send(DatabaseRowEvent::InsertRow(InsertedRow {
row: row_order.clone(),
index: Some(index as i32),
is_new: true,
}));
let row = self.database.lock().get_row(&row_order.id);
if let Some(row) = row {
for view in self.database_views.editors().await {
@ -423,10 +409,6 @@ impl DatabaseEditor {
let row = self.database.lock().remove_row(row_id);
if let Some(row) = row {
tracing::trace!("Did delete row:{:?}", row);
let _ = self
.row_event_tx
.send(DatabaseRowEvent::DeleteRow(row.id.clone()));
for view in self.database_views.editors().await {
view.v_did_delete_row(&row).await;
}
@ -481,15 +463,18 @@ impl DatabaseEditor {
}?;
(
field,
database.get_cell(field_id, &row_id).map(|cell| cell.cell),
database
.get_cell(field_id, &row_id)
.map(|row_cell| row_cell.cell),
)
};
let cell_changeset = cell_changeset.to_cell_changeset_str();
let new_cell =
apply_cell_data_changeset(cell_changeset, cell, &field, Some(self.cell_cache.clone()))?;
apply_cell_changeset(cell_changeset, cell, &field, Some(self.cell_cache.clone()))?;
self.update_cell(view_id, row_id, field_id, new_cell).await
}
/// Update a cell in the database.
/// This will notify all views that the cell has been updated.
pub async fn update_cell(
&self,
view_id: &str,
@ -497,6 +482,7 @@ impl DatabaseEditor {
field_id: &str,
new_cell: Cell,
) -> FlowyResult<()> {
// Get the old row before updating the cell. It would be better to get the old cell
let old_row = { self.database.lock().get_row(&row_id) };
self.database.lock().update_row(&row_id, |row_update| {
row_update.update_cells(|cell_update| {
@ -506,14 +492,8 @@ impl DatabaseEditor {
let option_row = self.database.lock().get_row(&row_id);
if let Some(new_row) = option_row {
let _ = self
.row_event_tx
.send(DatabaseRowEvent::UpdateRow(UpdatedRow {
row: RowOrder::from(&new_row),
field_ids: vec![field_id.to_string()],
}));
for view in self.database_views.editors().await {
view.v_did_update_row(&old_row, &new_row).await;
view.v_did_update_row(&old_row, &new_row, field_id).await;
}
}

View File

@ -5,7 +5,7 @@ use std::sync::Arc;
use collab_database::database::{gen_database_filter_id, gen_database_sort_id};
use collab_database::fields::Field;
use collab_database::rows::{Cells, Row, RowCell, RowId};
use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting};
use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting, RowOrder};
use tokio::sync::{broadcast, RwLock};
use flowy_error::{FlowyError, FlowyResult};
@ -20,7 +20,7 @@ use crate::entities::{
};
use crate::notification::{send_notification, DatabaseNotification};
use crate::services::cell::CellCache;
use crate::services::database::{database_view_setting_pb_from_view, DatabaseRowEvent};
use crate::services::database::{database_view_setting_pb_from_view, DatabaseRowEvent, UpdatedRow};
use crate::services::database_view::view_filter::make_filter_controller;
use crate::services::database_view::view_group::{
get_cell_for_row, get_cells_for_field, new_group_controller, new_group_controller_with_field,
@ -55,6 +55,7 @@ pub trait DatabaseViewData: Send + Sync + 'static {
/// Returns the `index` and `RowRevision` with row_id
fn get_row(&self, view_id: &str, row_id: &RowId) -> Fut<Option<(usize, Arc<Row>)>>;
/// Returns all the rows in the view
fn get_rows(&self, view_id: &str) -> Fut<Vec<Arc<Row>>>;
fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Fut<Vec<Arc<RowCell>>>;
@ -180,7 +181,12 @@ impl DatabaseViewEditor {
pub async fn v_did_create_row(&self, row: &Row, group_id: &Option<String>, index: usize) {
// Send the group notification if the current view has groups
match group_id.as_ref() {
None => {},
None => {
let changeset = RowsChangesetPB::from_insert(self.view_id.clone(), vec![row.into()]);
send_notification(&self.view_id, DatabaseNotification::DidUpdateViewRows)
.payload(changeset)
.send();
},
Some(group_id) => {
self
.group_controller
@ -213,9 +219,17 @@ impl DatabaseViewEditor {
notify_did_update_group_rows(changeset).await;
}
}
let changeset =
RowsChangesetPB::from_delete(self.view_id.clone(), vec![row.id.clone().into_inner()]);
send_notification(&self.view_id, DatabaseNotification::DidUpdateViewRows)
.payload(changeset)
.send();
}
pub async fn v_did_update_row(&self, old_row: &Option<Row>, row: &Row) {
/// Notify the view that the row has been updated. If the view has groups,
/// send the group notification with [GroupRowsNotificationPB]. Otherwise,
/// send the view notification with [RowsChangesetPB]
pub async fn v_did_update_row(&self, old_row: &Option<Row>, row: &Row, field_id: &str) {
let result = self
.mut_group_controller(|group_controller, field| {
Ok(group_controller.did_update_group_row(old_row, row, &field))
@ -244,20 +258,35 @@ impl DatabaseViewEditor {
for changeset in result.row_changesets {
notify_did_update_group_rows(changeset).await;
}
} else {
let update_row = UpdatedRow {
row: RowOrder::from(row),
field_ids: vec![field_id.to_string()],
};
let changeset = RowsChangesetPB::from_update(self.view_id.clone(), vec![update_row.into()]);
send_notification(&self.view_id, DatabaseNotification::DidUpdateViewRows)
.payload(changeset)
.send();
}
let filter_controller = self.filter_controller.clone();
let sort_controller = self.sort_controller.clone();
// Each row update will trigger a filter and sort operation. We don't want
// to block the main thread, so we spawn a new task to do the work.
let row_id = row.id.clone();
let weak_filter_controller = Arc::downgrade(&self.filter_controller);
let weak_sort_controller = Arc::downgrade(&self.sort_controller);
tokio::spawn(async move {
filter_controller
.did_receive_row_changed(row_id.clone())
.await;
sort_controller
.read()
.await
.did_receive_row_changed(row_id)
.await;
if let Some(filter_controller) = weak_filter_controller.upgrade() {
filter_controller
.did_receive_row_changed(row_id.clone())
.await;
}
if let Some(sort_controller) = weak_sort_controller.upgrade() {
sort_controller
.read()
.await
.did_receive_row_changed(row_id)
.await;
}
});
}
@ -513,7 +542,7 @@ impl DatabaseViewEditor {
}
/// Returns the current calendar settings
#[tracing::instrument(level = "debug", skip(self))]
#[tracing::instrument(level = "trace", skip(self))]
pub async fn v_get_layout_settings(&self, layout_ty: &DatabaseLayout) -> LayoutSettingParams {
let mut layout_setting = LayoutSettingParams::default();
match layout_ty {
@ -751,7 +780,7 @@ impl DatabaseViewEditor {
Some(events)
}
pub async fn handle_block_event(&self, event: Cow<'_, DatabaseRowEvent>) {
pub async fn handle_row_event(&self, event: Cow<'_, DatabaseRowEvent>) {
let changeset = match event.into_owned() {
DatabaseRowEvent::InsertRow(row) => {
RowsChangesetPB::from_insert(self.view_id.clone(), vec![row.into()])

View File

@ -1,4 +1,3 @@
use std::borrow::Cow;
use std::collections::HashMap;
use std::sync::Arc;
@ -31,10 +30,8 @@ impl DatabaseViews {
database: MutexDatabase,
cell_cache: CellCache,
database_view_data: Arc<dyn DatabaseViewData>,
row_event_rx: RowEventReceiver,
) -> FlowyResult<Self> {
let editor_map = Arc::new(RwLock::new(HashMap::default()));
listen_on_database_row_event(row_event_rx, editor_map.clone());
Ok(Self {
database,
database_view_data,
@ -126,26 +123,6 @@ impl DatabaseViews {
}
}
fn listen_on_database_row_event(
mut row_event_rx: broadcast::Receiver<DatabaseRowEvent>,
view_editors: Arc<RwLock<HashMap<String, Arc<DatabaseViewEditor>>>>,
) {
tokio::spawn(async move {
while let Ok(event) = row_event_rx.recv().await {
let read_guard = view_editors.read().await;
let view_editors = read_guard.values();
let event = if view_editors.len() == 1 {
Cow::Owned(event)
} else {
Cow::Borrowed(&event)
};
for view_editor in view_editors {
view_editor.handle_block_event(event.clone()).await;
}
}
});
}
pub fn gen_handler_id() -> String {
nanoid!(10)
}

View File

@ -7,10 +7,15 @@ use strum::IntoEnumIterator;
use strum_macros::EnumIter;
lazy_static! {
pub static ref CURRENCY_SYMBOL: Vec<String> = NumberFormat::iter()
pub static ref CURRENCY_SYMBOL: Vec<String> = sorted_symbol();
}
fn sorted_symbol() -> Vec<String> {
let mut symbols = NumberFormat::iter()
.map(|format| format.symbol())
.collect::<Vec<String>>();
pub static ref STRIP_SYMBOL: Vec<String> = vec![",".to_owned(), ".".to_owned()];
symbols.sort_by(|a, b| b.len().cmp(&a.len()));
symbols
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumIter, Serialize, Deserialize)]

View File

@ -47,13 +47,13 @@ mod tests {
};
for (num_str, visible) in [("123", true), ("1234", false), ("", false)] {
let data = NumberCellFormat::from_format_str(num_str, true, &NumberFormat::Num).unwrap();
let data = NumberCellFormat::from_format_str(num_str, &NumberFormat::Num).unwrap_or_default();
assert_eq!(number_filter.is_visible(&data), visible);
}
let format = NumberFormat::USD;
for (num_str, visible) in [("$123", true), ("1234", false), ("", false)] {
let data = NumberCellFormat::from_format_str(num_str, true, &format).unwrap();
let data = NumberCellFormat::from_format_str(num_str, &format).unwrap();
assert_eq!(number_filter.is_visible(&data), visible);
}
}
@ -64,7 +64,7 @@ mod tests {
content: "12".to_owned(),
};
for (num_str, visible) in [("123", true), ("10", false), ("30", true), ("", false)] {
let data = NumberCellFormat::from_format_str(num_str, true, &NumberFormat::Num).unwrap();
let data = NumberCellFormat::from_format_str(num_str, &NumberFormat::Num).unwrap_or_default();
assert_eq!(number_filter.is_visible(&data), visible);
}
}
@ -76,7 +76,7 @@ mod tests {
content: "100".to_owned(),
};
for (num_str, visible) in [("12", true), ("1234", false), ("30", true), ("", false)] {
let data = NumberCellFormat::from_format_str(num_str, true, &NumberFormat::Num).unwrap();
let data = NumberCellFormat::from_format_str(num_str, &NumberFormat::Num).unwrap_or_default();
assert_eq!(number_filter.is_visible(&data), visible);
}
}

View File

@ -1,16 +1,15 @@
#[cfg(test)]
mod tests {
use collab_database::fields::Field;
use strum::IntoEnumIterator;
use crate::entities::FieldType;
use crate::services::cell::CellDataDecoder;
use crate::services::field::{strip_currency_symbol, NumberFormat, NumberTypeOption};
use crate::services::field::{FieldBuilder, NumberCellData};
use crate::services::field::{NumberFormat, NumberTypeOption};
/// Testing when the input is not a number.
#[test]
fn number_type_option_invalid_input_test() {
fn number_type_option_input_test() {
let type_option = NumberTypeOption::default();
let field_type = FieldType::Number;
let field = FieldBuilder::from_field_type(field_type.clone()).build();
@ -20,492 +19,65 @@ mod tests {
// Input is letter
assert_number(&type_option, "abc", "", &field_type, &field);
assert_number(&type_option, "-123", "-123", &field_type, &field);
assert_number(&type_option, "abc-123", "-123", &field_type, &field);
assert_number(&type_option, "+123", "123", &field_type, &field);
assert_number(&type_option, "0.2", "0.2", &field_type, &field);
assert_number(&type_option, "-0.2", "-0.2", &field_type, &field);
assert_number(&type_option, "-$0.2", "0.2", &field_type, &field);
}
/// Testing the strip_currency_symbol function. It should return the string without the input symbol.
#[test]
fn number_type_option_strip_symbol_test() {
// Remove the $ symbol
assert_eq!(strip_currency_symbol("$18,443"), "18,443".to_owned());
// Remove the ¥ symbol
assert_eq!(strip_currency_symbol("¥0.2"), "0.2".to_owned());
}
/// Format the input number to the corresponding format string.
#[test]
fn number_type_option_format_number_test() {
let mut type_option = NumberTypeOption::default();
fn dollar_type_option_test() {
let field_type = FieldType::Number;
let field = FieldBuilder::from_field_type(field_type.clone()).build();
let mut type_option = NumberTypeOption::new();
type_option.format = NumberFormat::USD;
let field = FieldBuilder::new(field_type.clone(), type_option.clone()).build();
for format in NumberFormat::iter() {
type_option.format = format;
match format {
NumberFormat::Num => {
assert_number(&type_option, "18443", "18443", &field_type, &field);
},
NumberFormat::USD => {
assert_number(&type_option, "18443", "$18,443", &field_type, &field);
},
NumberFormat::CanadianDollar => {
assert_number(&type_option, "18443", "CA$18,443", &field_type, &field)
},
NumberFormat::EUR => assert_number(&type_option, "18443", "€18.443", &field_type, &field),
NumberFormat::Pound => assert_number(&type_option, "18443", "£18,443", &field_type, &field),
NumberFormat::Yen => {
assert_number(&type_option, "18443", "¥18,443", &field_type, &field);
},
NumberFormat::Ruble => {
assert_number(&type_option, "18443", "18.443RUB", &field_type, &field)
},
NumberFormat::Rupee => assert_number(&type_option, "18443", "₹18,443", &field_type, &field),
NumberFormat::Won => assert_number(&type_option, "18443", "₩18,443", &field_type, &field),
NumberFormat::Yuan => {
assert_number(&type_option, "18443", "CN¥18,443", &field_type, &field);
},
NumberFormat::Real => {
assert_number(&type_option, "18443", "R$18,443", &field_type, &field);
},
NumberFormat::Lira => {
assert_number(&type_option, "18443", "TRY18.443", &field_type, &field)
},
NumberFormat::Rupiah => {
assert_number(&type_option, "18443", "IDR18,443", &field_type, &field)
},
NumberFormat::Franc => {
assert_number(&type_option, "18443", "CHF18,443", &field_type, &field)
},
NumberFormat::HongKongDollar => {
assert_number(&type_option, "18443", "HZ$18,443", &field_type, &field)
},
NumberFormat::NewZealandDollar => {
assert_number(&type_option, "18443", "NZ$18,443", &field_type, &field)
},
NumberFormat::Krona => {
assert_number(&type_option, "18443", "18 443SEK", &field_type, &field)
},
NumberFormat::NorwegianKrone => {
assert_number(&type_option, "18443", "18,443NOK", &field_type, &field)
},
NumberFormat::MexicanPeso => {
assert_number(&type_option, "18443", "MX$18,443", &field_type, &field)
},
NumberFormat::Rand => {
assert_number(&type_option, "18443", "ZAR18,443", &field_type, &field)
},
NumberFormat::NewTaiwanDollar => {
assert_number(&type_option, "18443", "NT$18,443", &field_type, &field)
},
NumberFormat::DanishKrone => {
assert_number(&type_option, "18443", "18.443DKK", &field_type, &field)
},
NumberFormat::Baht => {
assert_number(&type_option, "18443", "THB18,443", &field_type, &field)
},
NumberFormat::Forint => {
assert_number(&type_option, "18443", "18 443HUF", &field_type, &field)
},
NumberFormat::Koruna => {
assert_number(&type_option, "18443", "18 443CZK", &field_type, &field)
},
NumberFormat::Shekel => {
assert_number(&type_option, "18443", "18 443Kč", &field_type, &field)
},
NumberFormat::ChileanPeso => {
assert_number(&type_option, "18443", "CLP18.443", &field_type, &field)
},
NumberFormat::PhilippinePeso => {
assert_number(&type_option, "18443", "₱18,443", &field_type, &field)
},
NumberFormat::Dirham => {
assert_number(&type_option, "18443", "18,443AED", &field_type, &field)
},
NumberFormat::ColombianPeso => {
assert_number(&type_option, "18443", "COP18.443", &field_type, &field)
},
NumberFormat::Riyal => {
assert_number(&type_option, "18443", "SAR18,443", &field_type, &field)
},
NumberFormat::Ringgit => {
assert_number(&type_option, "18443", "MYR18,443", &field_type, &field)
},
NumberFormat::Leu => assert_number(&type_option, "18443", "18.443RON", &field_type, &field),
NumberFormat::ArgentinePeso => {
assert_number(&type_option, "18443", "ARS18.443", &field_type, &field)
},
NumberFormat::UruguayanPeso => {
assert_number(&type_option, "18443", "UYU18.443", &field_type, &field)
},
NumberFormat::Percent => {
assert_number(&type_option, "18443", "18,443%", &field_type, &field)
},
}
}
assert_number(&type_option, "", "", &field_type, &field);
assert_number(&type_option, "abc", "", &field_type, &field);
assert_number(&type_option, "-123", "-$123", &field_type, &field);
assert_number(&type_option, "+123", "$123", &field_type, &field);
assert_number(&type_option, "0.2", "$0.2", &field_type, &field);
assert_number(&type_option, "-0.2", "-$0.2", &field_type, &field);
assert_number(&type_option, "-$0.2", "-$0.2", &field_type, &field);
assert_number(&type_option, "-€0.2", "-$0.2", &field_type, &field);
}
/// Format the input String to the corresponding format string.
#[test]
fn number_type_option_format_str_test() {
let mut type_option = NumberTypeOption::default();
fn dollar_type_option_test2() {
let field_type = FieldType::Number;
let field = FieldBuilder::from_field_type(field_type.clone()).build();
let mut type_option = NumberTypeOption::new();
type_option.format = NumberFormat::USD;
let field = FieldBuilder::new(field_type.clone(), type_option.clone()).build();
for format in NumberFormat::iter() {
type_option.format = format;
match format {
NumberFormat::Num => {
assert_number(&type_option, "18443", "18443", &field_type, &field);
assert_number(&type_option, "0.2", "0.2", &field_type, &field);
assert_number(&type_option, "", "", &field_type, &field);
assert_number(&type_option, "abc", "", &field_type, &field);
},
NumberFormat::USD => {
assert_number(&type_option, "$18,44", "$1,844", &field_type, &field);
assert_number(&type_option, "$0.2", "$0.2", &field_type, &field);
assert_number(&type_option, "$1844", "$1,844", &field_type, &field);
assert_number(&type_option, "1844", "$1,844", &field_type, &field);
},
NumberFormat::CanadianDollar => {
assert_number(&type_option, "CA$18,44", "CA$1,844", &field_type, &field);
assert_number(&type_option, "CA$0.2", "CA$0.2", &field_type, &field);
assert_number(&type_option, "CA$1844", "CA$1,844", &field_type, &field);
assert_number(&type_option, "1844", "CA$1,844", &field_type, &field);
},
NumberFormat::EUR => {
assert_number(&type_option, "€18.44", "€18,44", &field_type, &field);
assert_number(&type_option, "€0.5", "€0,5", &field_type, &field);
assert_number(&type_option, "€1844", "€1.844", &field_type, &field);
assert_number(&type_option, "1844", "€1.844", &field_type, &field);
},
NumberFormat::Pound => {
assert_number(&type_option, "£18,44", "£1,844", &field_type, &field);
assert_number(&type_option, "£0.2", "£0.2", &field_type, &field);
assert_number(&type_option, "£1844", "£1,844", &field_type, &field);
assert_number(&type_option, "1844", "£1,844", &field_type, &field);
},
NumberFormat::Yen => {
assert_number(&type_option, "¥18,44", "¥1,844", &field_type, &field);
assert_number(&type_option, "¥0.2", "¥0.2", &field_type, &field);
assert_number(&type_option, "¥1844", "¥1,844", &field_type, &field);
assert_number(&type_option, "1844", "¥1,844", &field_type, &field);
},
NumberFormat::Ruble => {
assert_number(&type_option, "RUB18.44", "18,44RUB", &field_type, &field);
assert_number(&type_option, "0.5", "0,5RUB", &field_type, &field);
assert_number(&type_option, "RUB1844", "1.844RUB", &field_type, &field);
assert_number(&type_option, "1844", "1.844RUB", &field_type, &field);
},
NumberFormat::Rupee => {
assert_number(&type_option, "₹18,44", "₹1,844", &field_type, &field);
assert_number(&type_option, "₹0.2", "₹0.2", &field_type, &field);
assert_number(&type_option, "₹1844", "₹1,844", &field_type, &field);
assert_number(&type_option, "1844", "₹1,844", &field_type, &field);
},
NumberFormat::Won => {
assert_number(&type_option, "₩18,44", "₩1,844", &field_type, &field);
assert_number(&type_option, "₩0.3", "₩0", &field_type, &field);
assert_number(&type_option, "₩1844", "₩1,844", &field_type, &field);
assert_number(&type_option, "1844", "₩1,844", &field_type, &field);
},
NumberFormat::Yuan => {
assert_number(&type_option, "CN¥18,44", "CN¥1,844", &field_type, &field);
assert_number(&type_option, "CN¥0.2", "CN¥0.2", &field_type, &field);
assert_number(&type_option, "CN¥1844", "CN¥1,844", &field_type, &field);
assert_number(&type_option, "1844", "CN¥1,844", &field_type, &field);
},
NumberFormat::Real => {
assert_number(&type_option, "R$18,44", "R$1,844", &field_type, &field);
assert_number(&type_option, "R$0.2", "R$0.2", &field_type, &field);
assert_number(&type_option, "R$1844", "R$1,844", &field_type, &field);
assert_number(&type_option, "1844", "R$1,844", &field_type, &field);
},
NumberFormat::Lira => {
assert_number(&type_option, "TRY18.44", "TRY18,44", &field_type, &field);
assert_number(&type_option, "TRY0.5", "TRY0,5", &field_type, &field);
assert_number(&type_option, "TRY1844", "TRY1.844", &field_type, &field);
assert_number(&type_option, "1844", "TRY1.844", &field_type, &field);
},
NumberFormat::Rupiah => {
assert_number(&type_option, "IDR18,44", "IDR1,844", &field_type, &field);
assert_number(&type_option, "IDR0.2", "IDR0.2", &field_type, &field);
assert_number(&type_option, "IDR1844", "IDR1,844", &field_type, &field);
assert_number(&type_option, "1844", "IDR1,844", &field_type, &field);
},
NumberFormat::Franc => {
assert_number(&type_option, "CHF18,44", "CHF1,844", &field_type, &field);
assert_number(&type_option, "CHF0.2", "CHF0.2", &field_type, &field);
assert_number(&type_option, "CHF1844", "CHF1,844", &field_type, &field);
assert_number(&type_option, "1844", "CHF1,844", &field_type, &field);
},
NumberFormat::HongKongDollar => {
assert_number(&type_option, "HZ$18,44", "HZ$1,844", &field_type, &field);
assert_number(&type_option, "HZ$0.2", "HZ$0.2", &field_type, &field);
assert_number(&type_option, "HZ$1844", "HZ$1,844", &field_type, &field);
assert_number(&type_option, "1844", "HZ$1,844", &field_type, &field);
},
NumberFormat::NewZealandDollar => {
assert_number(&type_option, "NZ$18,44", "NZ$1,844", &field_type, &field);
assert_number(&type_option, "NZ$0.2", "NZ$0.2", &field_type, &field);
assert_number(&type_option, "NZ$1844", "NZ$1,844", &field_type, &field);
assert_number(&type_option, "1844", "NZ$1,844", &field_type, &field);
},
NumberFormat::Krona => {
assert_number(&type_option, "SEK18,44", "18,44SEK", &field_type, &field);
assert_number(&type_option, "SEK0.2", "0,2SEK", &field_type, &field);
assert_number(&type_option, "SEK1844", "1 844SEK", &field_type, &field);
assert_number(&type_option, "1844", "1 844SEK", &field_type, &field);
},
NumberFormat::NorwegianKrone => {
assert_number(&type_option, "NOK18,44", "1,844NOK", &field_type, &field);
assert_number(&type_option, "NOK0.2", "0.2NOK", &field_type, &field);
assert_number(&type_option, "NOK1844", "1,844NOK", &field_type, &field);
assert_number(&type_option, "1844", "1,844NOK", &field_type, &field);
},
NumberFormat::MexicanPeso => {
assert_number(&type_option, "MX$18,44", "MX$1,844", &field_type, &field);
assert_number(&type_option, "MX$0.2", "MX$0.2", &field_type, &field);
assert_number(&type_option, "MX$1844", "MX$1,844", &field_type, &field);
assert_number(&type_option, "1844", "MX$1,844", &field_type, &field);
},
NumberFormat::Rand => {
assert_number(&type_option, "ZAR18,44", "ZAR1,844", &field_type, &field);
assert_number(&type_option, "ZAR0.2", "ZAR0.2", &field_type, &field);
assert_number(&type_option, "ZAR1844", "ZAR1,844", &field_type, &field);
assert_number(&type_option, "1844", "ZAR1,844", &field_type, &field);
},
NumberFormat::NewTaiwanDollar => {
assert_number(&type_option, "NT$18,44", "NT$1,844", &field_type, &field);
assert_number(&type_option, "NT$0.2", "NT$0.2", &field_type, &field);
assert_number(&type_option, "NT$1844", "NT$1,844", &field_type, &field);
assert_number(&type_option, "1844", "NT$1,844", &field_type, &field);
},
NumberFormat::DanishKrone => {
assert_number(&type_option, "DKK18.44", "18,44DKK", &field_type, &field);
assert_number(&type_option, "DKK0.5", "0,5DKK", &field_type, &field);
assert_number(&type_option, "DKK1844", "1.844DKK", &field_type, &field);
assert_number(&type_option, "1844", "1.844DKK", &field_type, &field);
},
NumberFormat::Baht => {
assert_number(&type_option, "THB18,44", "THB1,844", &field_type, &field);
assert_number(&type_option, "THB0.2", "THB0.2", &field_type, &field);
assert_number(&type_option, "THB1844", "THB1,844", &field_type, &field);
assert_number(&type_option, "1844", "THB1,844", &field_type, &field);
},
NumberFormat::Forint => {
assert_number(&type_option, "HUF18,44", "18HUF", &field_type, &field);
assert_number(&type_option, "HUF0.3", "0HUF", &field_type, &field);
assert_number(&type_option, "HUF1844", "1 844HUF", &field_type, &field);
assert_number(&type_option, "1844", "1 844HUF", &field_type, &field);
},
NumberFormat::Koruna => {
assert_number(&type_option, "CZK18,44", "18,44CZK", &field_type, &field);
assert_number(&type_option, "CZK0.2", "0,2CZK", &field_type, &field);
assert_number(&type_option, "CZK1844", "1 844CZK", &field_type, &field);
assert_number(&type_option, "1844", "1 844CZK", &field_type, &field);
},
NumberFormat::Shekel => {
assert_number(&type_option, "Kč18,44", "18,44Kč", &field_type, &field);
assert_number(&type_option, "Kč0.2", "0,2Kč", &field_type, &field);
assert_number(&type_option, "Kč1844", "1 844Kč", &field_type, &field);
assert_number(&type_option, "1844", "1 844Kč", &field_type, &field);
},
NumberFormat::ChileanPeso => {
assert_number(&type_option, "CLP18.44", "CLP18", &field_type, &field);
assert_number(&type_option, "0.5", "CLP0", &field_type, &field);
assert_number(&type_option, "CLP1844", "CLP1.844", &field_type, &field);
assert_number(&type_option, "1844", "CLP1.844", &field_type, &field);
},
NumberFormat::PhilippinePeso => {
assert_number(&type_option, "₱18,44", "₱1,844", &field_type, &field);
assert_number(&type_option, "₱0.2", "₱0.2", &field_type, &field);
assert_number(&type_option, "₱1844", "₱1,844", &field_type, &field);
assert_number(&type_option, "1844", "₱1,844", &field_type, &field);
},
NumberFormat::Dirham => {
assert_number(&type_option, "AED18,44", "1,844AED", &field_type, &field);
assert_number(&type_option, "AED0.2", "0.2AED", &field_type, &field);
assert_number(&type_option, "AED1844", "1,844AED", &field_type, &field);
assert_number(&type_option, "1844", "1,844AED", &field_type, &field);
},
NumberFormat::ColombianPeso => {
assert_number(&type_option, "COP18.44", "COP18,44", &field_type, &field);
assert_number(&type_option, "0.5", "COP0,5", &field_type, &field);
assert_number(&type_option, "COP1844", "COP1.844", &field_type, &field);
assert_number(&type_option, "1844", "COP1.844", &field_type, &field);
},
NumberFormat::Riyal => {
assert_number(&type_option, "SAR18,44", "SAR1,844", &field_type, &field);
assert_number(&type_option, "SAR0.2", "SAR0.2", &field_type, &field);
assert_number(&type_option, "SAR1844", "SAR1,844", &field_type, &field);
assert_number(&type_option, "1844", "SAR1,844", &field_type, &field);
},
NumberFormat::Ringgit => {
assert_number(&type_option, "MYR18,44", "MYR1,844", &field_type, &field);
assert_number(&type_option, "MYR0.2", "MYR0.2", &field_type, &field);
assert_number(&type_option, "MYR1844", "MYR1,844", &field_type, &field);
assert_number(&type_option, "1844", "MYR1,844", &field_type, &field);
},
NumberFormat::Leu => {
assert_number(&type_option, "RON18.44", "18,44RON", &field_type, &field);
assert_number(&type_option, "0.5", "0,5RON", &field_type, &field);
assert_number(&type_option, "RON1844", "1.844RON", &field_type, &field);
assert_number(&type_option, "1844", "1.844RON", &field_type, &field);
},
NumberFormat::ArgentinePeso => {
assert_number(&type_option, "ARS18.44", "ARS18,44", &field_type, &field);
assert_number(&type_option, "0.5", "ARS0,5", &field_type, &field);
assert_number(&type_option, "ARS1844", "ARS1.844", &field_type, &field);
assert_number(&type_option, "1844", "ARS1.844", &field_type, &field);
},
NumberFormat::UruguayanPeso => {
assert_number(&type_option, "UYU18.44", "UYU18,44", &field_type, &field);
assert_number(&type_option, "0.5", "UYU0,5", &field_type, &field);
assert_number(&type_option, "UYU1844", "UYU1.844", &field_type, &field);
assert_number(&type_option, "1844", "UYU1.844", &field_type, &field);
},
NumberFormat::Percent => {
assert_number(&type_option, "1", "1%", &field_type, &field);
assert_number(&type_option, "10.1", "10.1%", &field_type, &field);
assert_number(&type_option, "100", "100%", &field_type, &field);
},
}
}
assert_number(
&type_option,
"99999999999",
"$99,999,999,999",
&field_type,
&field,
);
assert_number(
&type_option,
"$99,999,999,999",
"$99,999,999,999",
&field_type,
&field,
);
}
/// Carry out the sign positive to input number
#[test]
fn number_description_sign_test() {
let mut type_option = NumberTypeOption {
sign_positive: false,
..Default::default()
};
fn other_symbol_to_dollar_type_option_test() {
let field_type = FieldType::Number;
let field = FieldBuilder::from_field_type(field_type.clone()).build();
let mut type_option = NumberTypeOption::new();
type_option.format = NumberFormat::USD;
let field = FieldBuilder::new(field_type.clone(), type_option.clone()).build();
for format in NumberFormat::iter() {
type_option.format = format;
match format {
NumberFormat::Num => {
assert_number(&type_option, "18443", "18443", &field_type, &field);
},
NumberFormat::USD => {
assert_number(&type_option, "18443", "-$18,443", &field_type, &field);
},
NumberFormat::CanadianDollar => {
assert_number(&type_option, "18443", "-CA$18,443", &field_type, &field)
},
NumberFormat::EUR => assert_number(&type_option, "18443", "-€18.443", &field_type, &field),
NumberFormat::Pound => {
assert_number(&type_option, "18443", "-£18,443", &field_type, &field)
},
NumberFormat::Yen => {
assert_number(&type_option, "18443", "-¥18,443", &field_type, &field);
},
NumberFormat::Ruble => {
assert_number(&type_option, "18443", "-18.443RUB", &field_type, &field)
},
NumberFormat::Rupee => {
assert_number(&type_option, "18443", "-₹18,443", &field_type, &field)
},
NumberFormat::Won => assert_number(&type_option, "18443", "-₩18,443", &field_type, &field),
NumberFormat::Yuan => {
assert_number(&type_option, "18443", "-CN¥18,443", &field_type, &field);
},
NumberFormat::Real => {
assert_number(&type_option, "18443", "-R$18,443", &field_type, &field);
},
NumberFormat::Lira => {
assert_number(&type_option, "18443", "-TRY18.443", &field_type, &field)
},
NumberFormat::Rupiah => {
assert_number(&type_option, "18443", "-IDR18,443", &field_type, &field)
},
NumberFormat::Franc => {
assert_number(&type_option, "18443", "-CHF18,443", &field_type, &field)
},
NumberFormat::HongKongDollar => {
assert_number(&type_option, "18443", "-HZ$18,443", &field_type, &field)
},
NumberFormat::NewZealandDollar => {
assert_number(&type_option, "18443", "-NZ$18,443", &field_type, &field)
},
NumberFormat::Krona => {
assert_number(&type_option, "18443", "-18 443SEK", &field_type, &field)
},
NumberFormat::NorwegianKrone => {
assert_number(&type_option, "18443", "-18,443NOK", &field_type, &field)
},
NumberFormat::MexicanPeso => {
assert_number(&type_option, "18443", "-MX$18,443", &field_type, &field)
},
NumberFormat::Rand => {
assert_number(&type_option, "18443", "-ZAR18,443", &field_type, &field)
},
NumberFormat::NewTaiwanDollar => {
assert_number(&type_option, "18443", "-NT$18,443", &field_type, &field)
},
NumberFormat::DanishKrone => {
assert_number(&type_option, "18443", "-18.443DKK", &field_type, &field)
},
NumberFormat::Baht => {
assert_number(&type_option, "18443", "-THB18,443", &field_type, &field)
},
NumberFormat::Forint => {
assert_number(&type_option, "18443", "-18 443HUF", &field_type, &field)
},
NumberFormat::Koruna => {
assert_number(&type_option, "18443", "-18 443CZK", &field_type, &field)
},
NumberFormat::Shekel => {
assert_number(&type_option, "18443", "-18 443Kč", &field_type, &field)
},
NumberFormat::ChileanPeso => {
assert_number(&type_option, "18443", "-CLP18.443", &field_type, &field)
},
NumberFormat::PhilippinePeso => {
assert_number(&type_option, "18443", "-₱18,443", &field_type, &field)
},
NumberFormat::Dirham => {
assert_number(&type_option, "18443", "-18,443AED", &field_type, &field)
},
NumberFormat::ColombianPeso => {
assert_number(&type_option, "18443", "-COP18.443", &field_type, &field)
},
NumberFormat::Riyal => {
assert_number(&type_option, "18443", "-SAR18,443", &field_type, &field)
},
NumberFormat::Ringgit => {
assert_number(&type_option, "18443", "-MYR18,443", &field_type, &field)
},
NumberFormat::Leu => {
assert_number(&type_option, "18443", "-18.443RON", &field_type, &field)
},
NumberFormat::ArgentinePeso => {
assert_number(&type_option, "18443", "-ARS18.443", &field_type, &field)
},
NumberFormat::UruguayanPeso => {
assert_number(&type_option, "18443", "-UYU18.443", &field_type, &field)
},
NumberFormat::Percent => {
assert_number(&type_option, "18443", "-18,443%", &field_type, &field)
},
}
}
assert_number(&type_option, "€0.2", "$0.2", &field_type, &field);
assert_number(&type_option, "-€0.2", "-$0.2", &field_type, &field);
assert_number(&type_option, "-CN¥0.2", "-$0.2", &field_type, &field);
assert_number(&type_option, "CN¥0.2", "$0.2", &field_type, &field);
assert_number(&type_option, "0.2", "$0.2", &field_type, &field);
}
fn assert_number(
@ -513,14 +85,14 @@ mod tests {
input_str: &str,
expected_str: &str,
field_type: &FieldType,
field_rev: &Field,
field: &Field,
) {
assert_eq!(
type_option
.decode_cell_str(
&NumberCellData(input_str.to_owned()).into(),
field_type,
field_rev
field
)
.unwrap()
.to_string(),

View File

@ -27,7 +27,6 @@ pub struct NumberTypeOption {
pub format: NumberFormat,
pub scale: u32,
pub symbol: String,
pub sign_positive: bool,
pub name: String,
}
@ -75,13 +74,11 @@ impl From<TypeOptionData> for NumberTypeOption {
.unwrap_or_default();
let scale = data.get_i64_value("scale").unwrap_or_default() as u32;
let symbol = data.get_str_value("symbol").unwrap_or_default();
let sign_positive = data.get_bool_value("sign_positive").unwrap_or_default();
let name = data.get_str_value("name").unwrap_or_default();
Self {
format,
scale,
symbol,
sign_positive,
name,
}
}
@ -92,7 +89,6 @@ impl From<NumberTypeOption> for TypeOptionData {
TypeOptionDataBuilder::new()
.insert_i64_value("format", data.format.value())
.insert_i64_value("scale", data.scale as i64)
.insert_bool_value("sign_positive", data.sign_positive)
.insert_str_value("name", data.name)
.insert_str_value("symbol", data.symbol)
.build()
@ -132,20 +128,23 @@ impl NumberTypeOption {
Err(_) => Ok(NumberCellFormat::new()),
}
} else {
let num = match EXTRACT_NUM_REGEX.captures(&num_cell_data.0) {
let num_str = match EXTRACT_NUM_REGEX.captures(&num_cell_data.0) {
Ok(Some(captures)) => captures
.get(0)
.map(|m| m.as_str().to_string())
.unwrap_or_default(),
_ => "".to_string(),
};
match Decimal::from_str(&num) {
Ok(value, ..) => Ok(NumberCellFormat::from_decimal(value)),
match Decimal::from_str(&num_str) {
Ok(decimal, ..) => {
return Ok(NumberCellFormat::from_decimal(decimal));
},
Err(_) => Ok(NumberCellFormat::new()),
}
}
},
_ => NumberCellFormat::from_format_str(&num_cell_data.0, self.sign_positive, &self.format),
_ => NumberCellFormat::from_format_str(&num_cell_data.0, &self.format),
}
}
@ -155,17 +154,6 @@ impl NumberTypeOption {
}
}
pub(crate) fn strip_currency_symbol<T: ToString>(s: T) -> String {
let mut s = s.to_string();
for symbol in CURRENCY_SYMBOL.iter() {
if s.starts_with(symbol) {
s = s.strip_prefix(symbol).unwrap_or("").to_string();
break;
}
}
s
}
impl TypeOptionTransform for NumberTypeOption {}
impl CellDataDecoder for NumberTypeOption {
@ -206,9 +194,11 @@ impl CellDataChangeset for NumberTypeOption {
changeset: <Self as TypeOption>::CellChangeset,
_cell: Option<Cell>,
) -> FlowyResult<(Cell, <Self as TypeOption>::CellData)> {
let number_cell_data = NumberCellData(changeset.trim().to_string());
let num_str = changeset.trim().to_string();
let number_cell_data = NumberCellData(num_str);
let formatter = self.format_cell_data(&number_cell_data)?;
tracing::trace!("number: {:?}", number_cell_data);
match self.format {
NumberFormat::Num => Ok((
NumberCellData(formatter.to_string()).into(),
@ -245,9 +235,8 @@ impl TypeOptionCellDataCompare for NumberTypeOption {
cell_data: &<Self as TypeOption>::CellData,
other_cell_data: &<Self as TypeOption>::CellData,
) -> Ordering {
let left = NumberCellFormat::from_format_str(&cell_data.0, self.sign_positive, &self.format);
let right =
NumberCellFormat::from_format_str(&other_cell_data.0, self.sign_positive, &self.format);
let left = NumberCellFormat::from_format_str(&cell_data.0, &self.format);
let right = NumberCellFormat::from_format_str(&other_cell_data.0, &self.format);
match (left, right) {
(Ok(left), Ok(right)) => {
return left.decimal().cmp(right.decimal());
@ -266,7 +255,6 @@ impl std::default::Default for NumberTypeOption {
format,
scale: 0,
symbol,
sign_positive: true,
name: "Number".to_string(),
}
}
@ -274,5 +262,5 @@ impl std::default::Default for NumberTypeOption {
lazy_static! {
static ref SCIENTIFIC_NOTATION_REGEX: Regex = Regex::new(r"([+-]?\d*\.?\d+)e([+-]?\d+)").unwrap();
static ref EXTRACT_NUM_REGEX: Regex = Regex::new(r"-?\d+(\.\d+)?").unwrap();
pub(crate) static ref EXTRACT_NUM_REGEX: Regex = Regex::new(r"-?\d+(\.\d+)?").unwrap();
}

View File

@ -1,6 +1,6 @@
use crate::services::cell::{CellBytesCustomParser, CellProtobufBlobParser, DecodedCellData};
use crate::services::field::number_currency::Currency;
use crate::services::field::{strip_currency_symbol, NumberFormat, STRIP_SYMBOL};
use crate::services::field::{NumberFormat, EXTRACT_NUM_REGEX};
use bytes::Bytes;
use flowy_error::FlowyResult;
use rust_decimal::Decimal;
@ -21,29 +21,28 @@ impl NumberCellFormat {
}
}
pub fn from_format_str(s: &str, sign_positive: bool, format: &NumberFormat) -> FlowyResult<Self> {
let mut num_str = strip_currency_symbol(s);
let currency = format.currency();
/// The num_str might contain currency symbol, e.g. $1,000.00
pub fn from_format_str(num_str: &str, format: &NumberFormat) -> FlowyResult<Self> {
if num_str.is_empty() {
return Ok(Self::default());
}
// If the first char is not '-', then it is a sign.
let sign_positive = match num_str.find("-") {
None => true,
Some(offset) => offset != 0,
};
// Extract number from string.
let num_str = extract_number(num_str);
match Decimal::from_str(&num_str) {
Ok(mut decimal) => {
decimal.set_sign_positive(sign_positive);
let money = Money::from_decimal(decimal, currency);
let money = Money::from_decimal(decimal, format.currency());
Ok(Self::from_money(money))
},
Err(_) => match Money::from_str(&num_str, currency) {
Ok(money) => Ok(NumberCellFormat::from_money(money)),
Err(_) => {
num_str.retain(|c| !STRIP_SYMBOL.contains(&c.to_string()));
if num_str.chars().all(char::is_numeric) {
Self::from_format_str(&num_str, sign_positive, format)
} else {
// returns empty string if it can be formatted
Ok(Self::default())
}
},
Err(_) => match Money::from_str(&num_str, format.currency()) {
Ok(money) => Ok(Self::from_money(money)),
Err(_) => Ok(Self::default()),
},
}
}
@ -71,17 +70,14 @@ impl NumberCellFormat {
}
}
// impl FromStr for NumberCellData {
// type Err = FlowyError;
//
// fn from_str(s: &str) -> Result<Self, Self::Err> {
// if s.is_empty() {
// return Ok(Self::default());
// }
// let decimal = Decimal::from_str(s).map_err(internal_error)?;
// Ok(Self::from_decimal(decimal))
// }
// }
fn extract_number(num_str: &str) -> String {
let mut matches = EXTRACT_NUM_REGEX.find_iter(num_str);
let mut values = vec![];
while let Some(Ok(m)) = matches.next() {
values.push(m.as_str().to_string());
}
values.join("")
}
impl ToString for NumberCellFormat {
fn to_string(&self) -> String {
@ -108,7 +104,7 @@ impl CellProtobufBlobParser for NumberCellDataParser {
type Object = NumberCellFormat;
fn parser(bytes: &Bytes) -> FlowyResult<Self::Object> {
match String::from_utf8(bytes.to_vec()) {
Ok(s) => NumberCellFormat::from_format_str(&s, true, &NumberFormat::Num),
Ok(s) => NumberCellFormat::from_format_str(&s, &NumberFormat::Num),
Err(_) => Ok(NumberCellFormat::default()),
}
}
@ -119,7 +115,7 @@ impl CellBytesCustomParser for NumberCellCustomDataParser {
type Object = NumberCellFormat;
fn parse(&self, bytes: &Bytes) -> FlowyResult<Self::Object> {
match String::from_utf8(bytes.to_vec()) {
Ok(s) => NumberCellFormat::from_format_str(&s, true, &self.0),
Ok(s) => NumberCellFormat::from_format_str(&s, &self.0),
Err(_) => Ok(NumberCellFormat::default()),
}
}

View File

@ -141,12 +141,12 @@ where
let cell_data = self.decode_cell_str(cell, decoded_field_type, field)?;
if let Some(cell_data_cache) = self.cell_data_cache.as_ref() {
tracing::trace!(
"Cell cache update: field_type:{}, cell: {:?}, cell_data: {:?}",
decoded_field_type,
cell,
cell_data
);
// tracing::trace!(
// "Cell cache update: field_type:{}, cell: {:?}, cell_data: {:?}",
// decoded_field_type,
// cell,
// cell_data
// );
cell_data_cache
.write()
.insert(key.as_ref(), cell_data.clone());
@ -163,12 +163,12 @@ where
if let Some(cell_data_cache) = self.cell_data_cache.as_ref() {
let field_type = FieldType::from(field.field_type);
let key = CellDataCacheKey::new(field, field_type.clone(), cell);
tracing::trace!(
"Cell cache update: field_type:{}, cell: {:?}, cell_data: {:?}",
field_type,
cell,
cell_data
);
// tracing::trace!(
// "Cell cache update: field_type:{}, cell: {:?}, cell_data: {:?}",
// field_type,
// cell,
// cell_data
// );
cell_data_cache.write().insert(key.as_ref(), cell_data);
}
}

View File

@ -25,7 +25,7 @@ use std::sync::Arc;
/// * `configuration_writer`: as writer used to write the group configuration to disk
///
#[tracing::instrument(
level = "debug",
level = "trace",
skip_all,
fields(grouping_field_id=%grouping_field.id, grouping_field_type)
err

View File

@ -85,7 +85,7 @@ impl SortController {
self.gen_task(task_type, QualityOfService::Background).await;
}
#[tracing::instrument(name = "process_sort_task", level = "trace", skip_all, err)]
#[tracing::instrument(name = "process_sort_task", level = "debug", skip_all, err)]
pub async fn process(&mut self, predicate: &str) -> FlowyResult<()> {
let event_type = SortEvent::from_str(predicate).unwrap();
let mut rows = self.delegate.get_rows(&self.view_id).await;

View File

@ -3,7 +3,6 @@ use lib_dispatch::prelude::AFPluginData;
use crate::entities::NetworkStatePB;
#[tracing::instrument(level = "debug", skip_all)]
pub async fn update_network_ty(_data: AFPluginData<NetworkStatePB>) -> Result<(), FlowyError> {
Ok(())
}

View File

@ -25,6 +25,7 @@ thread-id = "3.3.0"
log = "0.4"
bytes = "1.4"
nanoid = "0.4.0"
tempdir = "0.3.7"
[dev-dependencies]
quickcheck = "1.0.3"

View File

@ -9,7 +9,8 @@ use flowy_user::{
event_map::UserEvent::{InitUser, SignIn, SignOut, SignUp},
};
use lib_dispatch::prelude::{AFPluginDispatcher, AFPluginRequest, ToBytes};
use std::{fs, path::PathBuf, sync::Arc};
use std::sync::Arc;
pub struct ViewTest {
pub sdk: FlowySDKTest,
@ -118,21 +119,6 @@ async fn create_view(
.parse::<ViewPB>()
}
pub fn root_dir() -> String {
// https://doc.rust-lang.org/cargo/reference/environment-variables.html
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| "./".to_owned());
let mut path_buf = fs::canonicalize(&PathBuf::from(&manifest_dir)).unwrap();
path_buf.pop(); // rust-lib
path_buf.push("temp");
path_buf.push("flowy");
let root_dir = path_buf.to_str().unwrap().to_string();
if !std::path::Path::new(&root_dir).exists() {
std::fs::create_dir_all(&root_dir).unwrap();
}
root_dir
}
pub fn random_email() -> String {
format!("{}@appflowy.io", nanoid!(20))
}

View File

@ -1,4 +1,5 @@
use nanoid::nanoid;
use std::env::temp_dir;
use flowy_core::{AppFlowyCore, AppFlowyCoreConfig};
use flowy_net::http_server::self_host::configuration::get_client_server_configuration;
@ -37,8 +38,8 @@ impl std::default::Default for FlowySDKTest {
impl FlowySDKTest {
pub fn new() -> Self {
let server_config = get_client_server_configuration().unwrap();
let config =
AppFlowyCoreConfig::new(&root_dir(), nanoid!(6), server_config).log_filter("info", vec![]);
let config = AppFlowyCoreConfig::new(temp_dir().to_str().unwrap(), nanoid!(6), server_config)
.log_filter("info", vec![]);
let sdk = std::thread::spawn(|| AppFlowyCore::new(config))
.join()
.unwrap();