Forráskód Böngészése

feat: config grid editor

appflowy 3 éve
szülő
commit
9125db7ef0

+ 1 - 1
frontend/rust-lib/Cargo.lock

@@ -1049,12 +1049,12 @@ dependencies = [
 name = "flowy-grid"
 version = "0.1.0"
 dependencies = [
- "async-trait",
  "bytes",
  "chrono",
  "dart-notify",
  "diesel",
  "flowy-collaboration",
+ "flowy-database",
  "flowy-derive",
  "flowy-error",
  "flowy-grid-data-model",

+ 2 - 1
frontend/rust-lib/flowy-database/migrations/2021-09-22-074638_flowy-doc-op/down.sql

@@ -1 +1,2 @@
--- This file should undo anything in `up.sql`
+-- This file should undo anything in `up.sql`
+DROP TABLE rev_table;

+ 2 - 0
frontend/rust-lib/flowy-database/migrations/2022-03-04-101530_flowy-grid/down.sql

@@ -0,0 +1,2 @@
+-- This file should undo anything in `up.sql`
+DROP TABLE kv_table;

+ 5 - 0
frontend/rust-lib/flowy-database/migrations/2022-03-04-101530_flowy-grid/up.sql

@@ -0,0 +1,5 @@
+-- Your SQL goes here
+CREATE TABLE kv_table (
+   key TEXT NOT NULL PRIMARY KEY,
+   value BLOB NOT NULL DEFAULT (x'')
+);

+ 3 - 5
frontend/rust-lib/flowy-database/src/lib.rs

@@ -6,12 +6,10 @@ pub mod kv;
 
 use lib_sqlite::PoolConfig;
 pub use lib_sqlite::{ConnectionPool, DBConnection, Database};
-
 pub mod schema;
 
 #[macro_use]
 pub mod macros;
-
 #[macro_use]
 extern crate diesel;
 #[macro_use]
@@ -20,11 +18,11 @@ extern crate diesel_derives;
 extern crate diesel_migrations;
 
 pub type Error = diesel::result::Error;
-
 pub mod prelude {
-    pub use diesel::{query_dsl::*, BelongingToDsl, ExpressionMethods, RunQueryDsl};
-
     pub use super::UserDatabaseConnection;
+    pub use crate::*;
+    pub use diesel::SqliteConnection;
+    pub use diesel::{query_dsl::*, BelongingToDsl, ExpressionMethods, RunQueryDsl};
 }
 
 embed_migrations!("../flowy-database/migrations/");

+ 8 - 0
frontend/rust-lib/flowy-database/src/schema.rs

@@ -21,6 +21,13 @@ table! {
     }
 }
 
+table! {
+    kv_table (key) {
+        key -> Text,
+        value -> Binary,
+    }
+}
+
 table! {
     rev_table (id) {
         id -> Integer,
@@ -84,6 +91,7 @@ table! {
 allow_tables_to_appear_in_same_query!(
     app_table,
     doc_table,
+    kv_table,
     rev_table,
     trash_table,
     user_table,

+ 3 - 2
frontend/rust-lib/flowy-grid/Cargo.toml

@@ -10,12 +10,13 @@ lib-dispatch = { path = "../lib-dispatch" }
 dart-notify = { path = "../dart-notify" }
 lib-sqlite = { path = "../lib-sqlite" }
 flowy-sync = { path = "../flowy-sync" }
-flowy-error = { path = "../flowy-error"}
+flowy-error = { path = "../flowy-error", features = ["db"]}
 flowy-derive = { path = "../../../shared-lib/flowy-derive" }
 lib-ot = { path = "../../../shared-lib/lib-ot" }
 lib-infra = { path = "../../../shared-lib/lib-infra" }
 flowy-grid-data-model = { path = "../../../shared-lib/flowy-grid-data-model" }
 flowy-collaboration = { path = "../../../shared-lib/flowy-collaboration" }
+flowy-database = { path = "../flowy-database" }
 
 strum = "0.21"
 strum_macros = "0.21"
@@ -27,8 +28,8 @@ lazy_static = "1.4.0"
 chrono = "0.4.19"
 uuid = { version = "0.8", features = ["serde", "v4"] }
 bytes = { version = "1.0" }
-async-trait = "0.1.52"
 diesel = {version = "1.4.8", features = ["sqlite"]}
+#diesel_derives = {version = "1.4.1", features = ["sqlite"]}
 
 
 parking_lot = "0.11"

+ 24 - 12
frontend/rust-lib/flowy-grid/src/services/grid_editor.rs

@@ -1,9 +1,9 @@
-use crate::services::row_kv::{RowKVPersistence, RowKVTransaction};
+use crate::services::kv_persistence::{GridKVPersistence, KVTransaction};
 use flowy_collaboration::client_grid::{GridChange, GridPad};
 use flowy_collaboration::entities::revision::Revision;
 use flowy_collaboration::util::make_delta_from_revisions;
 use flowy_error::{FlowyError, FlowyResult};
-use flowy_grid_data_model::entities::{GridId, RawRow};
+use flowy_grid_data_model::entities::{Field, GridId, RawRow};
 use flowy_sync::{
     RevisionCloudService, RevisionCompact, RevisionManager, RevisionObjectBuilder, RevisionPersistence,
     RevisionWebSocket, RevisionWebSocketManager,
@@ -19,7 +19,7 @@ pub struct ClientGridEditor {
     grid_id: GridId,
     grid: Arc<RwLock<GridPad>>,
     rev_manager: Arc<RevisionManager>,
-    kv: Arc<RowKVPersistence>,
+    kv: Arc<GridKVPersistence>,
 }
 
 impl ClientGridEditor {
@@ -39,7 +39,7 @@ impl ClientGridEditor {
             rev_manager.load::<GridPadBuilder, GridRevisionCompact>(cloud).await?,
         ));
         let rev_manager = Arc::new(rev_manager);
-        let kv = Arc::new(RowKVPersistence::new(pool));
+        let kv = Arc::new(GridKVPersistence::new(pool));
 
         let user_id = user_id.to_owned();
         let grid_id = grid_id.to_owned();
@@ -53,18 +53,30 @@ impl ClientGridEditor {
     }
 
     pub async fn create_row(&self, row: RawRow) -> FlowyResult<()> {
-        let _ = self
-            .modify(|grid| {
-                let change = grid.create_row(&row)?;
-                Ok(change)
-            })
-            .await?;
-
+        let _ = self.modify(|grid| Ok(grid.create_row(&row)?)).await?;
         let _ = self.kv.set(row)?;
         Ok(())
     }
 
-    pub async fn modify<F>(&self, f: F) -> FlowyResult<()>
+    pub async fn delete_rows(&self, ids: Vec<String>) -> FlowyResult<()> {
+        let _ = self.modify(|grid| Ok(grid.delete_rows(&ids)?)).await?;
+        // let _ = self.kv.batch_delete(ids)?;
+        Ok(())
+    }
+
+    pub async fn create_field(&mut self, field: Field) -> FlowyResult<()> {
+        let _ = self.modify(|grid| Ok(grid.create_field(&field)?)).await?;
+        let _ = self.kv.set(field)?;
+        Ok(())
+    }
+
+    pub async fn delete_field(&mut self, field_id: &str) -> FlowyResult<()> {
+        let _ = self.modify(|grid| Ok(grid.delete_field(field_id)?)).await?;
+        // let _ = self.kv.remove(field_id)?;
+        Ok(())
+    }
+
+    async fn modify<F>(&self, f: F) -> FlowyResult<()>
     where
         F: FnOnce(&mut GridPad) -> FlowyResult<Option<GridChange>>,
     {

+ 167 - 0
frontend/rust-lib/flowy-grid/src/services/kv_persistence.rs

@@ -0,0 +1,167 @@
+use ::diesel::{query_dsl::*, ExpressionMethods};
+use bytes::Bytes;
+use diesel::SqliteConnection;
+use flowy_database::{
+    prelude::*,
+    schema::{kv_table, kv_table::dsl},
+};
+use flowy_error::{FlowyError, FlowyResult};
+use flowy_grid_data_model::entities::{Field, RawRow};
+use lib_infra::future::{BoxResultFuture, FutureResult};
+use lib_sqlite::{ConnectionManager, ConnectionPool};
+use std::sync::Arc;
+
+#[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)]
+#[table_name = "kv_table"]
+#[primary_key(key)]
+pub struct KeyValue {
+    key: String,
+    value: Vec<u8>,
+}
+
+pub trait KVTransaction {
+    fn get<T: TryFrom<KeyValue, Error = FlowyError>>(&self, key: &str) -> FlowyResult<Option<T>>;
+    fn set<T: Into<KeyValue>>(&self, value: T) -> FlowyResult<()>;
+    fn remove(&self, key: &str) -> FlowyResult<()>;
+
+    fn batch_get<T: TryFrom<KeyValue, Error = FlowyError>>(&self, keys: Vec<String>) -> FlowyResult<Vec<T>>;
+    fn batch_set<T: Into<KeyValue>>(&self, values: Vec<T>) -> FlowyResult<()>;
+    fn batch_remove(&self, keys: Vec<String>) -> FlowyResult<()>;
+}
+
+pub struct GridKVPersistence {
+    pool: Arc<ConnectionPool>,
+}
+
+impl GridKVPersistence {
+    pub fn new(pool: Arc<ConnectionPool>) -> Self {
+        Self { pool }
+    }
+
+    pub fn begin_transaction<F, O>(&self, f: F) -> FlowyResult<O>
+    where
+        F: for<'a> FnOnce(SqliteTransaction<'a>) -> FlowyResult<O>,
+    {
+        let conn = self.pool.get()?;
+        conn.immediate_transaction::<_, FlowyError, _>(|| {
+            let sql_transaction = SqliteTransaction { conn: &conn };
+            f(sql_transaction)
+        })
+    }
+}
+
+impl KVTransaction for GridKVPersistence {
+    fn get<T: TryFrom<KeyValue, Error = FlowyError>>(&self, key: &str) -> FlowyResult<Option<T>> {
+        self.begin_transaction(|transaction| transaction.get(key))
+    }
+
+    fn set<T: Into<KeyValue>>(&self, value: T) -> FlowyResult<()> {
+        self.begin_transaction(|transaction| transaction.set(value))
+    }
+
+    fn remove(&self, key: &str) -> FlowyResult<()> {
+        self.begin_transaction(|transaction| transaction.remove(key))
+    }
+
+    fn batch_get<T: TryFrom<KeyValue, Error = FlowyError>>(&self, keys: Vec<String>) -> FlowyResult<Vec<T>> {
+        self.begin_transaction(|transaction| transaction.batch_get(keys))
+    }
+
+    fn batch_set<T: Into<KeyValue>>(&self, values: Vec<T>) -> FlowyResult<()> {
+        self.begin_transaction(|transaction| transaction.batch_set(values))
+    }
+
+    fn batch_remove(&self, keys: Vec<String>) -> FlowyResult<()> {
+        self.begin_transaction(|transaction| transaction.batch_remove(keys))
+    }
+}
+
+pub struct SqliteTransaction<'a> {
+    conn: &'a SqliteConnection,
+}
+
+impl<'a> KVTransaction for SqliteTransaction<'a> {
+    fn get<T: TryFrom<KeyValue, Error = FlowyError>>(&self, key: &str) -> FlowyResult<Option<T>> {
+        let item = dsl::kv_table
+            .filter(kv_table::key.eq(key))
+            .first::<KeyValue>(self.conn)?;
+        let value: T = item.try_into()?;
+        Ok(Some(value))
+    }
+
+    fn set<T: Into<KeyValue>>(&self, value: T) -> FlowyResult<()> {
+        let item: KeyValue = value.into();
+        let _ = diesel::replace_into(kv_table::table).values(&item).execute(self.conn)?;
+        Ok(())
+    }
+
+    fn remove(&self, key: &str) -> FlowyResult<()> {
+        let sql = dsl::kv_table.filter(kv_table::key.eq(key));
+        let _ = diesel::delete(sql).execute(self.conn)?;
+        Ok(())
+    }
+
+    fn batch_get<T: TryFrom<KeyValue, Error = FlowyError>>(&self, keys: Vec<String>) -> FlowyResult<Vec<T>> {
+        let items = dsl::kv_table
+            .filter(kv_table::key.eq_any(&keys))
+            .load::<KeyValue>(self.conn)?;
+        let mut values = vec![];
+        for item in items {
+            let value: T = item.try_into()?;
+            values.push(value);
+        }
+        Ok(values)
+    }
+
+    fn batch_set<T: Into<KeyValue>>(&self, values: Vec<T>) -> FlowyResult<()> {
+        let items = values.into_iter().map(|value| value.into()).collect::<Vec<KeyValue>>();
+        let _ = diesel::replace_into(kv_table::table)
+            .values(&items)
+            .execute(self.conn)?;
+        Ok(())
+    }
+
+    fn batch_remove(&self, keys: Vec<String>) -> FlowyResult<()> {
+        let sql = dsl::kv_table.filter(kv_table::key.eq_any(keys));
+        let _ = diesel::delete(sql).execute(self.conn)?;
+        Ok(())
+    }
+}
+
+impl std::convert::From<RawRow> for KeyValue {
+    fn from(row: RawRow) -> Self {
+        let key = row.id.clone();
+        let bytes: Bytes = row.try_into().unwrap();
+        let value = bytes.to_vec();
+        KeyValue { key, value }
+    }
+}
+
+impl std::convert::TryInto<RawRow> for KeyValue {
+    type Error = FlowyError;
+
+    fn try_into(self) -> Result<RawRow, Self::Error> {
+        let bytes = Bytes::from(self.value);
+        RawRow::try_from(bytes)
+            .map_err(|e| FlowyError::internal().context(format!("Deserialize into raw row failed: {:?}", e)))
+    }
+}
+
+impl std::convert::From<Field> for KeyValue {
+    fn from(field: Field) -> Self {
+        let key = field.id.clone();
+        let bytes: Bytes = field.try_into().unwrap();
+        let value = bytes.to_vec();
+        KeyValue { key, value }
+    }
+}
+
+impl std::convert::TryInto<Field> for KeyValue {
+    type Error = FlowyError;
+
+    fn try_into(self) -> Result<Field, Self::Error> {
+        let bytes = Bytes::from(self.value);
+        Field::try_from(bytes)
+            .map_err(|e| FlowyError::internal().context(format!("Deserialize into field failed: {:?}", e)))
+    }
+}

+ 1 - 1
frontend/rust-lib/flowy-grid/src/services/mod.rs

@@ -3,6 +3,6 @@ mod util;
 
 pub mod cell_data;
 pub mod grid_editor;
-mod row_kv;
+mod kv_persistence;
 
 pub use stringify::*;

+ 0 - 95
frontend/rust-lib/flowy-grid/src/services/row_kv.rs

@@ -1,95 +0,0 @@
-use async_trait::async_trait;
-use diesel::SqliteConnection;
-use flowy_error::{FlowyError, FlowyResult};
-use flowy_grid_data_model::entities::RawRow;
-use lib_infra::future::{BoxResultFuture, FutureResult};
-use lib_sqlite::{ConnectionManager, ConnectionPool};
-use std::sync::Arc;
-
-pub trait RowKVTransaction {
-    fn get(&self, row_id: &str) -> FlowyResult<Option<RawRow>>;
-    fn set(&self, row: RawRow) -> FlowyResult<()>;
-    fn remove(&self, row_id: &str) -> FlowyResult<()>;
-
-    fn batch_get(&self, ids: Vec<String>) -> FlowyResult<()>;
-    fn batch_set(&self, rows: Vec<RawRow>) -> FlowyResult<()>;
-    fn batch_delete(&self, ids: Vec<String>) -> FlowyResult<()>;
-}
-
-pub struct RowKVPersistence {
-    pool: Arc<ConnectionPool>,
-}
-
-impl RowKVPersistence {
-    pub fn new(pool: Arc<ConnectionPool>) -> Self {
-        Self { pool }
-    }
-
-    pub fn begin_transaction<F, O>(&self, f: F) -> FlowyResult<O>
-    where
-        F: for<'a> FnOnce(Box<dyn RowKVTransaction + 'a>) -> FlowyResult<O>,
-    {
-        let conn = self.pool.get()?;
-        conn.immediate_transaction::<_, FlowyError, _>(|| {
-            let sql_transaction = SqliteTransaction { conn: &conn };
-            f(Box::new(sql_transaction))
-        })
-    }
-}
-
-impl RowKVTransaction for RowKVPersistence {
-    fn get(&self, row_id: &str) -> FlowyResult<Option<RawRow>> {
-        self.begin_transaction(|transaction| transaction.get(row_id))
-    }
-
-    fn set(&self, row: RawRow) -> FlowyResult<()> {
-        self.begin_transaction(|transaction| transaction.set(row))
-    }
-
-    fn remove(&self, row_id: &str) -> FlowyResult<()> {
-        self.begin_transaction(|transaction| transaction.remove(row_id))
-    }
-
-    fn batch_get(&self, ids: Vec<String>) -> FlowyResult<()> {
-        self.begin_transaction(|transaction| transaction.batch_get(ids))
-    }
-
-    fn batch_set(&self, rows: Vec<RawRow>) -> FlowyResult<()> {
-        self.begin_transaction(|transaction| transaction.batch_set(rows))
-    }
-
-    fn batch_delete(&self, ids: Vec<String>) -> FlowyResult<()> {
-        self.begin_transaction(|transaction| transaction.batch_delete(ids))
-    }
-}
-
-pub struct SqliteTransaction<'a> {
-    conn: &'a SqliteConnection,
-}
-
-#[async_trait]
-impl<'a> RowKVTransaction for SqliteTransaction<'a> {
-    fn get(&self, row_id: &str) -> FlowyResult<Option<RawRow>> {
-        todo!()
-    }
-
-    fn set(&self, row: RawRow) -> FlowyResult<()> {
-        todo!()
-    }
-
-    fn remove(&self, row_id: &str) -> FlowyResult<()> {
-        todo!()
-    }
-
-    fn batch_get(&self, ids: Vec<String>) -> FlowyResult<()> {
-        todo!()
-    }
-
-    fn batch_set(&self, rows: Vec<RawRow>) -> FlowyResult<()> {
-        todo!()
-    }
-
-    fn batch_delete(&self, ids: Vec<String>) -> FlowyResult<()> {
-        todo!()
-    }
-}

+ 1 - 1
frontend/rust-lib/flowy-net/src/http_server/document.rs

@@ -2,8 +2,8 @@ use crate::{
     configuration::*,
     request::{HttpRequestBuilder, ResponseMiddleware},
 };
+use flowy_block::BlockCloudService;
 use flowy_collaboration::entities::document_info::{BlockId, BlockInfo, CreateBlockParams, ResetBlockParams};
-use flowy_document::BlockCloudService;
 use flowy_error::FlowyError;
 use http_flowy::response::FlowyResponse;
 use lazy_static::lazy_static;

+ 5 - 10
shared-lib/flowy-collaboration/src/client_grid/grid_pad.rs

@@ -54,16 +54,11 @@ impl GridPad {
         })
     }
 
-    pub fn delete_row(&mut self, row_id: &str) -> CollaborateResult<Option<GridChange>> {
-        self.modify_grid(
-            |grid| match grid.row_orders.iter().position(|row_order| row_order.row_id == row_id) {
-                None => Ok(None),
-                Some(index) => {
-                    grid.row_orders.remove(index);
-                    Ok(Some(()))
-                }
-            },
-        )
+    pub fn delete_rows(&mut self, row_ids: &Vec<String>) -> CollaborateResult<Option<GridChange>> {
+        self.modify_grid(|grid| {
+            grid.row_orders.retain(|row_order| !row_ids.contains(&row_order.row_id));
+            Ok(Some(()))
+        })
     }
 
     pub fn delete_field(&mut self, field_id: &str) -> CollaborateResult<Option<GridChange>> {