mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: config grid editor
This commit is contained in:
parent
49807a0b57
commit
9125db7ef0
2
frontend/rust-lib/Cargo.lock
generated
2
frontend/rust-lib/Cargo.lock
generated
@ -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",
|
||||
|
@ -1 +1,2 @@
|
||||
-- This file should undo anything in `up.sql`
|
||||
-- This file should undo anything in `up.sql`
|
||||
DROP TABLE rev_table;
|
@ -0,0 +1,2 @@
|
||||
-- This file should undo anything in `up.sql`
|
||||
DROP TABLE kv_table;
|
@ -0,0 +1,5 @@
|
||||
-- Your SQL goes here
|
||||
CREATE TABLE kv_table (
|
||||
key TEXT NOT NULL PRIMARY KEY,
|
||||
value BLOB NOT NULL DEFAULT (x'')
|
||||
);
|
@ -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/");
|
||||
|
@ -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,
|
||||
|
@ -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"
|
||||
|
@ -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
frontend/rust-lib/flowy-grid/src/services/kv_persistence.rs
Normal file
167
frontend/rust-lib/flowy-grid/src/services/kv_persistence.rs
Normal file
@ -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)))
|
||||
}
|
||||
}
|
@ -3,6 +3,6 @@ mod util;
|
||||
|
||||
pub mod cell_data;
|
||||
pub mod grid_editor;
|
||||
mod row_kv;
|
||||
mod kv_persistence;
|
||||
|
||||
pub use stringify::*;
|
||||
|
@ -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!()
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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>> {
|
||||
|
Loading…
Reference in New Issue
Block a user