feat: integrate grid into flowy-sdk

This commit is contained in:
appflowy 2022-03-04 22:09:16 +08:00
parent 9125db7ef0
commit d0b457c007
21 changed files with 233 additions and 26 deletions

View File

@ -12,10 +12,12 @@ import 'package:protobuf/protobuf.dart' as $pb;
class WSChannel extends $pb.ProtobufEnum {
static const WSChannel Document = WSChannel._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Document');
static const WSChannel Folder = WSChannel._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Folder');
static const WSChannel Grid = WSChannel._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Grid');
static const $core.List<WSChannel> values = <WSChannel> [
Document,
Folder,
Grid,
];
static final $core.Map<$core.int, WSChannel> _byValue = $pb.ProtobufEnum.initByValue(values);

View File

@ -14,11 +14,12 @@ const WSChannel$json = const {
'2': const [
const {'1': 'Document', '2': 0},
const {'1': 'Folder', '2': 1},
const {'1': 'Grid', '2': 2},
],
};
/// Descriptor for `WSChannel`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List wSChannelDescriptor = $convert.base64Decode('CglXU0NoYW5uZWwSDAoIRG9jdW1lbnQQABIKCgZGb2xkZXIQAQ==');
final $typed_data.Uint8List wSChannelDescriptor = $convert.base64Decode('CglXU0NoYW5uZWwSDAoIRG9jdW1lbnQQABIKCgZGb2xkZXIQARIICgRHcmlkEAI=');
@$core.Deprecated('Use webSocketRawMessageDescriptor instead')
const WebSocketRawMessage$json = const {
'1': 'WebSocketRawMessage',

View File

@ -1052,6 +1052,7 @@ dependencies = [
"bytes",
"chrono",
"dart-notify",
"dashmap",
"diesel",
"flowy-collaboration",
"flowy-database",

View File

@ -32,11 +32,11 @@ impl BlockManager {
block_user: Arc<dyn BlockUser>,
rev_web_socket: Arc<dyn RevisionWebSocket>,
) -> Self {
let block_handlers = Arc::new(BlockEditors::new());
let block_editors = Arc::new(BlockEditors::new());
Self {
cloud_service,
rev_web_socket,
block_editors: block_handlers,
block_editors,
block_user,
}
}

View File

@ -29,7 +29,7 @@ chrono = "0.4.19"
uuid = { version = "0.8", features = ["serde", "v4"] }
bytes = { version = "1.0" }
diesel = {version = "1.4.8", features = ["sqlite"]}
#diesel_derives = {version = "1.4.1", features = ["sqlite"]}
dashmap = "4.0"
parking_lot = "0.11"

View File

@ -1 +0,0 @@
pub struct GridManager {}

View File

@ -1,4 +1,4 @@
use crate::controller::GridManager;
use crate::manager::GridManager;
use flowy_error::FlowyError;
use flowy_grid_data_model::entities::{
CreateGridPayload, Grid, GridId, RepeatedField, RepeatedFieldOrder, RepeatedRow, RepeatedRowOrder,

View File

@ -1,5 +1,5 @@
use crate::controller::GridManager;
use crate::event_handler::*;
use crate::manager::GridManager;
use flowy_derive::{Flowy_Event, ProtoBuf_Enum};
use lib_dispatch::prelude::*;
use std::sync::Arc;

View File

@ -1,9 +1,9 @@
#[macro_use]
mod macros;
mod controller;
mod event_handler;
mod event_map;
pub mod event_map;
pub mod manager;
mod protobuf;
mod services;

View File

@ -0,0 +1,107 @@
use crate::services::grid_editor::ClientGridEditor;
use dashmap::DashMap;
use flowy_error::{FlowyError, FlowyResult};
use flowy_sync::{RevisionManager, RevisionPersistence, RevisionWebSocket};
use lib_sqlite::ConnectionPool;
use std::sync::Arc;
pub trait GridUser: Send + Sync {
fn user_id(&self) -> Result<String, FlowyError>;
fn token(&self) -> Result<String, FlowyError>;
fn db_pool(&self) -> Result<Arc<ConnectionPool>, FlowyError>;
}
pub struct GridManager {
grid_editors: Arc<GridEditors>,
grid_user: Arc<dyn GridUser>,
rev_web_socket: Arc<dyn RevisionWebSocket>,
}
impl GridManager {
pub fn new(grid_user: Arc<dyn GridUser>, rev_web_socket: Arc<dyn RevisionWebSocket>) -> Self {
let grid_editors = Arc::new(GridEditors::new());
Self {
grid_editors,
grid_user,
rev_web_socket,
}
}
#[tracing::instrument(level = "debug", skip(self, grid_id), fields(grid_id), err)]
pub async fn open_grid<T: AsRef<str>>(&self, grid_id: T) -> Result<Arc<ClientGridEditor>, FlowyError> {
let grid_id = grid_id.as_ref();
tracing::Span::current().record("grid_id", &grid_id);
self.get_grid_editor(grid_id).await
}
#[tracing::instrument(level = "trace", skip(self, grid_id), fields(grid_id), err)]
pub fn close_grid<T: AsRef<str>>(&self, grid_id: T) -> Result<(), FlowyError> {
let grid_id = grid_id.as_ref();
tracing::Span::current().record("grid_id", &grid_id);
self.grid_editors.remove(grid_id);
Ok(())
}
#[tracing::instrument(level = "debug", skip(self, grid_id), fields(doc_id), err)]
pub fn delete_grid<T: AsRef<str>>(&self, grid_id: T) -> Result<(), FlowyError> {
let grid_id = grid_id.as_ref();
tracing::Span::current().record("grid_id", &grid_id);
self.grid_editors.remove(grid_id);
Ok(())
}
async fn get_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<ClientGridEditor>> {
match self.grid_editors.get(grid_id) {
None => {
let db_pool = self.grid_user.db_pool()?;
self.make_grid_editor(grid_id, db_pool).await
}
Some(editor) => Ok(editor),
}
}
async fn make_grid_editor(
&self,
grid_id: &str,
pool: Arc<ConnectionPool>,
) -> Result<Arc<ClientGridEditor>, FlowyError> {
let token = self.grid_user.token()?;
let user_id = self.grid_user.user_id()?;
let grid_editor = ClientGridEditor::new(&user_id, grid_id, &token, pool, self.rev_web_socket.clone()).await?;
self.grid_editors.insert(grid_id, &grid_editor);
Ok(grid_editor)
}
}
pub struct GridEditors {
inner: DashMap<String, Arc<ClientGridEditor>>,
}
impl GridEditors {
fn new() -> Self {
Self { inner: DashMap::new() }
}
pub(crate) fn insert(&self, grid_id: &str, grid_editor: &Arc<ClientGridEditor>) {
if self.inner.contains_key(grid_id) {
tracing::warn!("Grid:{} already exists in cache", grid_id);
}
self.inner.insert(grid_id.to_string(), grid_editor.clone());
}
pub(crate) fn contains(&self, grid_id: &str) -> bool {
self.inner.get(grid_id).is_some()
}
pub(crate) fn get(&self, grid_id: &str) -> Option<Arc<ClientGridEditor>> {
if !self.contains(grid_id) {
return None;
}
let opened_grid = self.inner.get(grid_id).unwrap();
Some(opened_grid.clone())
}
pub(crate) fn remove(&self, grid_id: &str) {
self.inner.remove(grid_id);
}
}

View File

@ -16,7 +16,7 @@ use std::sync::Arc;
pub struct ClientGridEditor {
user_id: String,
grid_id: GridId,
grid_id: String,
grid: Arc<RwLock<GridPad>>,
rev_manager: Arc<RevisionManager>,
kv: Arc<GridKVPersistence>,
@ -25,13 +25,13 @@ pub struct ClientGridEditor {
impl ClientGridEditor {
pub async fn new(
user_id: &str,
grid_id: &GridId,
grid_id: &str,
token: &str,
pool: Arc<ConnectionPool>,
_web_socket: Arc<dyn RevisionWebSocket>,
) -> FlowyResult<Self> {
let rev_persistence = Arc::new(RevisionPersistence::new(user_id, grid_id.as_ref(), pool.clone()));
let mut rev_manager = RevisionManager::new(user_id, grid_id.as_ref(), rev_persistence);
) -> FlowyResult<Arc<Self>> {
let rev_persistence = Arc::new(RevisionPersistence::new(user_id, grid_id, pool.clone()));
let mut rev_manager = RevisionManager::new(user_id, grid_id, rev_persistence);
let cloud = Arc::new(GridRevisionCloudService {
token: token.to_string(),
});
@ -43,13 +43,13 @@ impl ClientGridEditor {
let user_id = user_id.to_owned();
let grid_id = grid_id.to_owned();
Ok(Self {
Ok(Arc::new(Self {
user_id,
grid_id,
grid,
rev_manager,
kv,
})
}))
}
pub async fn create_row(&self, row: RawRow) -> FlowyResult<()> {

View File

@ -122,6 +122,9 @@ impl LocalWebSocketRunner {
let _ = self.handle_folder_client_data(client_data, "".to_owned()).await?;
Ok(())
}
WSChannel::Grid => {
todo!("Implement grid web socket channel")
}
}
}

View File

@ -25,13 +25,13 @@ impl BlockDepsResolver {
server_config: &ClientServerConfiguration,
) -> Arc<BlockManager> {
let user = Arc::new(BlockUserImpl(user_session));
let ws_sender = Arc::new(BlockWebSocket(ws_conn.clone()));
let rev_web_socket = Arc::new(BlockWebSocket(ws_conn.clone()));
let cloud_service: Arc<dyn BlockCloudService> = match local_server {
None => Arc::new(BlockHttpCloudService::new(server_config.clone())),
Some(local_server) => local_server,
};
let manager = Arc::new(BlockManager::new(cloud_service, user, ws_sender));
let manager = Arc::new(BlockManager::new(cloud_service, user, rev_web_socket));
let receiver = Arc::new(DocumentWSMessageReceiverImpl(manager.clone()));
ws_conn.add_ws_message_receiver(receiver).unwrap();

View File

@ -0,0 +1,66 @@
use crate::FlowyError;
use bytes::Bytes;
use flowy_collaboration::entities::ws_data::ClientRevisionWSData;
use flowy_database::ConnectionPool;
use flowy_grid::manager::{GridManager, GridUser};
use flowy_net::ws::connection::FlowyWebSocketConnect;
use flowy_sync::{RevisionWebSocket, WSStateReceiver};
use flowy_user::services::UserSession;
use futures_core::future::BoxFuture;
use lib_infra::future::BoxResultFuture;
use lib_ws::{WSChannel, WebSocketRawMessage};
use std::convert::TryInto;
use std::sync::Arc;
pub struct GridDepsResolver();
impl GridDepsResolver {
pub fn resolve(ws_conn: Arc<FlowyWebSocketConnect>, user_session: Arc<UserSession>) -> Arc<GridManager> {
let user = Arc::new(GridUserImpl(user_session));
let rev_web_socket = Arc::new(GridWebSocket(ws_conn.clone()));
let manager = Arc::new(GridManager::new(user, rev_web_socket));
manager
}
}
struct GridUserImpl(Arc<UserSession>);
impl GridUser for GridUserImpl {
fn user_id(&self) -> Result<String, FlowyError> {
self.0.user_id()
}
fn token(&self) -> Result<String, FlowyError> {
self.0.token()
}
fn db_pool(&self) -> Result<Arc<ConnectionPool>, FlowyError> {
self.0.db_pool()
}
}
struct GridWebSocket(Arc<FlowyWebSocketConnect>);
impl RevisionWebSocket for GridWebSocket {
fn send(&self, data: ClientRevisionWSData) -> BoxResultFuture<(), FlowyError> {
let bytes: Bytes = data.try_into().unwrap();
let msg = WebSocketRawMessage {
channel: WSChannel::Grid,
data: bytes.to_vec(),
};
let ws_conn = self.0.clone();
Box::pin(async move {
match ws_conn.web_socket().await? {
None => {}
Some(sender) => {
sender.send(msg).map_err(|e| FlowyError::internal().context(e))?;
}
}
Ok(())
})
}
fn subscribe_state_changed(&self) -> BoxFuture<WSStateReceiver> {
let ws_conn = self.0.clone();
Box::pin(async move { ws_conn.subscribe_websocket_state().await })
}
}

View File

@ -1,7 +1,10 @@
mod block_deps;
mod folder_deps;
mod grid_deps;
mod user_deps;
mod util;
pub use block_deps::*;
pub use folder_deps::*;
pub use grid_deps::*;
pub use user_deps::*;

View File

@ -5,6 +5,7 @@ pub use flowy_net::get_client_server_configuration;
use crate::deps_resolve::*;
use flowy_block::BlockManager;
use flowy_folder::{controller::FolderManager, errors::FlowyError};
use flowy_grid::manager::GridManager;
use flowy_net::ClientServerConfiguration;
use flowy_net::{
entities::NetworkType,
@ -88,6 +89,7 @@ pub struct FlowySDK {
pub user_session: Arc<UserSession>,
pub document_manager: Arc<BlockManager>,
pub folder_manager: Arc<FolderManager>,
pub grid_manager: Arc<GridManager>,
pub dispatcher: Arc<EventDispatcher>,
pub ws_conn: Arc<FlowyWebSocketConnect>,
pub local_server: Option<Arc<LocalServer>>,
@ -100,7 +102,7 @@ impl FlowySDK {
tracing::debug!("🔥 {:?}", config);
let runtime = tokio_default_runtime().unwrap();
let (local_server, ws_conn) = mk_local_server(&config.server_config);
let (user_session, document_manager, folder_manager, local_server) = runtime.block_on(async {
let (user_session, document_manager, folder_manager, local_server, grid_manager) = runtime.block_on(async {
let user_session = mk_user_session(&config, &local_server, &config.server_config);
let document_manager = BlockDepsResolver::resolve(
local_server.clone(),
@ -118,15 +120,23 @@ impl FlowySDK {
)
.await;
let grid_manager = GridDepsResolver::resolve(ws_conn.clone(), user_session.clone());
if let Some(local_server) = local_server.as_ref() {
local_server.run();
}
ws_conn.init().await;
(user_session, document_manager, folder_manager, local_server)
(
user_session,
document_manager,
folder_manager,
local_server,
grid_manager,
)
});
let dispatcher = Arc::new(EventDispatcher::construct(runtime, || {
mk_modules(&ws_conn, &folder_manager, &user_session)
mk_modules(&ws_conn, &folder_manager, &grid_manager, &user_session)
}));
_start_listening(&dispatcher, &ws_conn, &user_session, &folder_manager);
@ -136,6 +146,7 @@ impl FlowySDK {
user_session,
document_manager,
folder_manager,
grid_manager,
dispatcher,
ws_conn,
local_server,

View File

@ -1,4 +1,5 @@
use flowy_folder::controller::FolderManager;
use flowy_grid::manager::GridManager;
use flowy_net::ws::connection::FlowyWebSocketConnect;
use flowy_user::services::UserSession;
use lib_dispatch::prelude::Module;
@ -7,22 +8,28 @@ use std::sync::Arc;
pub fn mk_modules(
ws_conn: &Arc<FlowyWebSocketConnect>,
folder_manager: &Arc<FolderManager>,
grid_manager: &Arc<GridManager>,
user_session: &Arc<UserSession>,
) -> Vec<Module> {
let user_module = mk_user_module(user_session.clone());
let folder_module = mk_folder_module(folder_manager.clone());
let network_module = mk_network_module(ws_conn.clone());
vec![user_module, folder_module, network_module]
let grid_module = mk_grid_module(grid_manager.clone());
vec![user_module, folder_module, network_module, grid_module]
}
fn mk_user_module(user_session: Arc<UserSession>) -> Module {
flowy_user::event_map::create(user_session)
}
fn mk_folder_module(core: Arc<FolderManager>) -> Module {
flowy_folder::event_map::create(core)
fn mk_folder_module(folder_manager: Arc<FolderManager>) -> Module {
flowy_folder::event_map::create(folder_manager)
}
fn mk_network_module(ws_conn: Arc<FlowyWebSocketConnect>) -> Module {
flowy_net::event_map::create(ws_conn)
}
fn mk_grid_module(grid_manager: Arc<GridManager>) -> Module {
flowy_grid::event_map::create(grid_manager)
}

View File

@ -12,10 +12,12 @@ pub struct WebSocketRawMessage {
pub data: Vec<u8>,
}
// The lib-ws crate should not contain business logic.So WSChannel should be removed into another place.
#[derive(ProtoBuf_Enum, Debug, Clone, Eq, PartialEq, Hash)]
pub enum WSChannel {
Document = 0,
Folder = 1,
Grid = 2,
}
impl std::default::Default for WSChannel {
@ -29,6 +31,7 @@ impl ToString for WSChannel {
match self {
WSChannel::Document => "0".to_string(),
WSChannel::Folder => "1".to_string(),
WSChannel::Grid => "2".to_string(),
}
}
}

View File

@ -217,6 +217,7 @@ impl ::protobuf::reflect::ProtobufValue for WebSocketRawMessage {
pub enum WSChannel {
Document = 0,
Folder = 1,
Grid = 2,
}
impl ::protobuf::ProtobufEnum for WSChannel {
@ -228,6 +229,7 @@ impl ::protobuf::ProtobufEnum for WSChannel {
match value {
0 => ::std::option::Option::Some(WSChannel::Document),
1 => ::std::option::Option::Some(WSChannel::Folder),
2 => ::std::option::Option::Some(WSChannel::Grid),
_ => ::std::option::Option::None
}
}
@ -236,6 +238,7 @@ impl ::protobuf::ProtobufEnum for WSChannel {
static values: &'static [WSChannel] = &[
WSChannel::Document,
WSChannel::Folder,
WSChannel::Grid,
];
values
}
@ -266,8 +269,8 @@ impl ::protobuf::reflect::ProtobufValue for WSChannel {
static file_descriptor_proto_data: &'static [u8] = b"\
\n\tmsg.proto\"O\n\x13WebSocketRawMessage\x12$\n\x07channel\x18\x01\x20\
\x01(\x0e2\n.WSChannelR\x07channel\x12\x12\n\x04data\x18\x02\x20\x01(\
\x0cR\x04data*%\n\tWSChannel\x12\x0c\n\x08Document\x10\0\x12\n\n\x06Fold\
er\x10\x01b\x06proto3\
\x0cR\x04data*/\n\tWSChannel\x12\x0c\n\x08Document\x10\0\x12\n\n\x06Fold\
er\x10\x01\x12\x08\n\x04Grid\x10\x02b\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -7,4 +7,5 @@ message WebSocketRawMessage {
enum WSChannel {
Document = 0;
Folder = 1;
Grid = 2;
}