mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge pull request #267 from AppFlowy-IO/rust_stable_channel
switch to rust stable channel
This commit is contained in:
commit
8fa3590661
4
.github/workflows/ci.yaml
vendored
4
.github/workflows/ci.yaml
vendored
@ -51,8 +51,8 @@ jobs:
|
|||||||
curl \
|
curl \
|
||||||
--proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
--proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
source $HOME/.cargo/env
|
source $HOME/.cargo/env
|
||||||
rustup toolchain install nightly
|
rustup toolchain install stable
|
||||||
rustup default nightly
|
rustup default stable
|
||||||
- name: Checkout Flutter
|
- name: Checkout Flutter
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
|
8
.github/workflows/frontend_rust.yml
vendored
8
.github/workflows/frontend_rust.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: stable
|
||||||
override: true
|
override: true
|
||||||
- run: rustup component add rustfmt
|
- run: rustup component add rustfmt
|
||||||
working-directory: frontend/rust-lib
|
working-directory: frontend/rust-lib
|
||||||
@ -38,7 +38,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: stable
|
||||||
override: true
|
override: true
|
||||||
- run: rustup component add clippy
|
- run: rustup component add clippy
|
||||||
working-directory: frontend/rust-lib
|
working-directory: frontend/rust-lib
|
||||||
@ -55,8 +55,8 @@ jobs:
|
|||||||
curl \
|
curl \
|
||||||
--proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
--proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
source $HOME/.cargo/env
|
source $HOME/.cargo/env
|
||||||
rustup toolchain install nightly
|
rustup toolchain install stable
|
||||||
rustup default nightly
|
rustup default stable
|
||||||
- name: Frontend tests
|
- name: Frontend tests
|
||||||
working-directory: frontend/rust-lib
|
working-directory: frontend/rust-lib
|
||||||
run: cargo test
|
run: cargo test
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
[toolchain]
|
|
||||||
channel = "nightly-2021-04-24"
|
|
2
backend/rust-toolchain.toml
Normal file
2
backend/rust-toolchain.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "stable-2022-01-20"
|
@ -1,18 +1,18 @@
|
|||||||
# https://rust-lang.github.io/rustfmt/?version=master&search=
|
# https://rust-lang.github.io/rustfmt/?version=master&search=
|
||||||
max_width = 120
|
max_width = 120
|
||||||
tab_spaces = 4
|
tab_spaces = 4
|
||||||
fn_single_line = true
|
# fn_single_line = true
|
||||||
match_block_trailing_comma = true
|
# match_block_trailing_comma = true
|
||||||
normalize_comments = true
|
# normalize_comments = true
|
||||||
wrap_comments = true
|
# wrap_comments = true
|
||||||
use_field_init_shorthand = true
|
# use_field_init_shorthand = true
|
||||||
use_try_shorthand = true
|
# use_try_shorthand = true
|
||||||
normalize_doc_attributes = true
|
# normalize_doc_attributes = true
|
||||||
report_todo = "Never"
|
# report_todo = "Never"
|
||||||
report_fixme = "Always"
|
# report_fixme = "Always"
|
||||||
imports_layout = "HorizontalVertical"
|
# imports_layout = "HorizontalVertical"
|
||||||
imports_granularity = "Crate"
|
# imports_granularity = "Crate"
|
||||||
reorder_modules = true
|
# reorder_modules = true
|
||||||
reorder_imports = true
|
# reorder_imports = true
|
||||||
enum_discrim_align_threshold = 20
|
# enum_discrim_align_threshold = 20
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
@ -8,8 +8,7 @@ use tokio::time::interval;
|
|||||||
use crate::{
|
use crate::{
|
||||||
config::{
|
config::{
|
||||||
env::{domain, secret, use_https},
|
env::{domain, secret, use_https},
|
||||||
DatabaseSettings,
|
DatabaseSettings, Settings,
|
||||||
Settings,
|
|
||||||
},
|
},
|
||||||
context::AppContext,
|
context::AppContext,
|
||||||
services::{
|
services::{
|
||||||
@ -34,9 +33,13 @@ impl Application {
|
|||||||
Ok(Self { port, server })
|
Ok(Self { port, server })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run_until_stopped(self) -> Result<(), std::io::Error> { self.server.await }
|
pub async fn run_until_stopped(self) -> Result<(), std::io::Error> {
|
||||||
|
self.server.await
|
||||||
|
}
|
||||||
|
|
||||||
pub fn port(&self) -> u16 { self.port }
|
pub fn port(&self) -> u16 {
|
||||||
|
self.port
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(listener: TcpListener, app_ctx: AppContext) -> Result<Server, std::io::Error> {
|
pub fn run(listener: TcpListener, app_ctx: AppContext) -> Result<Server, std::io::Error> {
|
||||||
@ -72,62 +75,63 @@ async fn period_check(_pool: PgPool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ws_scope() -> Scope { web::scope("/ws").service(crate::services::web_socket::router::establish_ws_connection) }
|
fn ws_scope() -> Scope {
|
||||||
|
web::scope("/ws").service(crate::services::web_socket::router::establish_ws_connection)
|
||||||
|
}
|
||||||
|
|
||||||
fn user_scope() -> Scope {
|
fn user_scope() -> Scope {
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP
|
||||||
// TODO: replace GET body with query params
|
// TODO: replace GET body with query params
|
||||||
web::scope("/api")
|
web::scope("/api")
|
||||||
// authentication
|
// authentication
|
||||||
.service(web::resource("/auth")
|
.service(
|
||||||
.route(web::post().to(user::sign_in_handler))
|
web::resource("/auth")
|
||||||
.route(web::delete().to(user::sign_out_handler))
|
.route(web::post().to(user::sign_in_handler))
|
||||||
|
.route(web::delete().to(user::sign_out_handler)),
|
||||||
)
|
)
|
||||||
.service(web::resource("/user")
|
.service(
|
||||||
.route(web::patch().to(user::set_user_profile_handler))
|
web::resource("/user")
|
||||||
.route(web::get().to(user::get_user_profile_handler))
|
.route(web::patch().to(user::set_user_profile_handler))
|
||||||
|
.route(web::get().to(user::get_user_profile_handler)),
|
||||||
)
|
)
|
||||||
.service(web::resource("/register")
|
.service(web::resource("/register").route(web::post().to(user::register_handler)))
|
||||||
.route(web::post().to(user::register_handler))
|
.service(
|
||||||
|
web::resource("/workspace")
|
||||||
|
.route(web::post().to(workspace::create_handler))
|
||||||
|
.route(web::delete().to(workspace::delete_handler))
|
||||||
|
.route(web::get().to(workspace::read_handler))
|
||||||
|
.route(web::patch().to(workspace::update_handler)),
|
||||||
)
|
)
|
||||||
.service(web::resource("/workspace")
|
.service(web::resource("/workspace_list/{user_id}").route(web::get().to(workspace::workspace_list)))
|
||||||
.route(web::post().to(workspace::create_handler))
|
.service(
|
||||||
.route(web::delete().to(workspace::delete_handler))
|
web::resource("/app")
|
||||||
.route(web::get().to(workspace::read_handler))
|
.route(web::post().to(app::create_handler))
|
||||||
.route(web::patch().to(workspace::update_handler))
|
.route(web::get().to(app::read_handler))
|
||||||
|
.route(web::delete().to(app::delete_handler))
|
||||||
|
.route(web::patch().to(app::update_handler)),
|
||||||
)
|
)
|
||||||
.service(web::resource("/workspace_list/{user_id}")
|
.service(
|
||||||
.route(web::get().to(workspace::workspace_list))
|
web::resource("/view")
|
||||||
|
.route(web::post().to(view::create_handler))
|
||||||
|
.route(web::delete().to(view::delete_handler))
|
||||||
|
.route(web::get().to(view::read_handler))
|
||||||
|
.route(web::patch().to(view::update_handler)),
|
||||||
)
|
)
|
||||||
.service(web::resource("/app")
|
.service(
|
||||||
.route(web::post().to(app::create_handler))
|
web::resource("/doc")
|
||||||
.route(web::get().to(app::read_handler))
|
.route(web::post().to(doc::create_document_handler))
|
||||||
.route(web::delete().to(app::delete_handler))
|
.route(web::get().to(doc::read_document_handler))
|
||||||
.route(web::patch().to(app::update_handler))
|
.route(web::patch().to(doc::reset_document_handler)),
|
||||||
)
|
)
|
||||||
.service(web::resource("/view")
|
.service(
|
||||||
.route(web::post().to(view::create_handler))
|
web::resource("/trash")
|
||||||
.route(web::delete().to(view::delete_handler))
|
.route(web::post().to(trash::create_handler))
|
||||||
.route(web::get().to(view::read_handler))
|
.route(web::delete().to(trash::delete_handler))
|
||||||
.route(web::patch().to(view::update_handler))
|
.route(web::get().to(trash::read_handler)),
|
||||||
)
|
|
||||||
.service(web::resource("/doc")
|
|
||||||
.route(web::post().to(doc::create_document_handler))
|
|
||||||
.route(web::get().to(doc::read_document_handler))
|
|
||||||
.route(web::patch().to(doc::reset_document_handler))
|
|
||||||
)
|
|
||||||
.service(web::resource("/trash")
|
|
||||||
.route(web::post().to(trash::create_handler))
|
|
||||||
.route(web::delete().to(trash::delete_handler))
|
|
||||||
.route(web::get().to(trash::read_handler))
|
|
||||||
)
|
|
||||||
.service(web::resource("/sync")
|
|
||||||
.route(web::post().to(trash::create_handler))
|
|
||||||
)
|
)
|
||||||
|
.service(web::resource("/sync").route(web::post().to(trash::create_handler)))
|
||||||
// password
|
// password
|
||||||
.service(web::resource("/password_change")
|
.service(web::resource("/password_change").route(web::post().to(user::change_password)))
|
||||||
.route(web::post().to(user::change_password))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn init_app_context(configuration: &Settings) -> AppContext {
|
pub async fn init_app_context(configuration: &Settings) -> AppContext {
|
||||||
|
@ -49,7 +49,9 @@ impl DatabaseSettings {
|
|||||||
.ssl_mode(ssl_mode)
|
.ssl_mode(ssl_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_db(&self) -> PgConnectOptions { self.without_db().database(&self.database_name) }
|
pub fn with_db(&self) -> PgConnectOptions {
|
||||||
|
self.without_db().database(&self.database_name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_configuration() -> Result<Settings, config::ConfigError> {
|
pub fn get_configuration() -> Result<Settings, config::ConfigError> {
|
||||||
|
@ -1,9 +1,17 @@
|
|||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
pub fn domain() -> String { env::var("DOMAIN").unwrap_or_else(|_| "localhost".to_string()) }
|
pub fn domain() -> String {
|
||||||
|
env::var("DOMAIN").unwrap_or_else(|_| "localhost".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn jwt_secret() -> String { env::var("JWT_SECRET").unwrap_or_else(|_| "my secret".into()) }
|
pub fn jwt_secret() -> String {
|
||||||
|
env::var("JWT_SECRET").unwrap_or_else(|_| "my secret".into())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn secret() -> String { env::var("SECRET_KEY").unwrap_or_else(|_| "0123".repeat(8)) }
|
pub fn secret() -> String {
|
||||||
|
env::var("SECRET_KEY").unwrap_or_else(|_| "0123".repeat(8))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn use_https() -> bool { false }
|
pub fn use_https() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
@ -56,7 +56,11 @@ pub struct FlowyPersistence {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FlowyPersistence {
|
impl FlowyPersistence {
|
||||||
pub fn pg_pool(&self) -> PgPool { self.pg_pool.clone() }
|
pub fn pg_pool(&self) -> PgPool {
|
||||||
|
self.pg_pool.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn kv_store(&self) -> Arc<DocumentKVPersistence> { self.kv_store.clone() }
|
pub fn kv_store(&self) -> Arc<DocumentKVPersistence> {
|
||||||
|
self.kv_store.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,9 @@ pub struct LoggedUser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<Claim> for LoggedUser {
|
impl std::convert::From<Claim> for LoggedUser {
|
||||||
fn from(c: Claim) -> Self { Self { user_id: c.user_id() } }
|
fn from(c: Claim) -> Self {
|
||||||
|
Self { user_id: c.user_id() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LoggedUser {
|
impl LoggedUser {
|
||||||
@ -61,7 +63,7 @@ impl std::convert::TryFrom<&HeaderValue> for LoggedUser {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Header to string failed: {:?}", e);
|
log::error!("Header to string failed: {:?}", e);
|
||||||
Err(ServerError::unauthorized())
|
Err(ServerError::unauthorized())
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,27 +78,31 @@ pub const EXPIRED_DURATION_DAYS: i64 = 30;
|
|||||||
|
|
||||||
pub struct AuthorizedUsers(DashMap<LoggedUser, AuthStatus>);
|
pub struct AuthorizedUsers(DashMap<LoggedUser, AuthStatus>);
|
||||||
impl std::default::Default for AuthorizedUsers {
|
impl std::default::Default for AuthorizedUsers {
|
||||||
fn default() -> Self { Self(DashMap::new()) }
|
fn default() -> Self {
|
||||||
|
Self(DashMap::new())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl AuthorizedUsers {
|
impl AuthorizedUsers {
|
||||||
pub fn new() -> Self { AuthorizedUsers::default() }
|
pub fn new() -> Self {
|
||||||
|
AuthorizedUsers::default()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_authorized(&self, user: &LoggedUser) -> bool {
|
pub fn is_authorized(&self, user: &LoggedUser) -> bool {
|
||||||
match self.0.get(user) {
|
match self.0.get(user) {
|
||||||
None => {
|
None => {
|
||||||
tracing::debug!("user not login yet or server was reboot");
|
tracing::debug!("user not login yet or server was reboot");
|
||||||
false
|
false
|
||||||
},
|
}
|
||||||
Some(status) => match *status {
|
Some(status) => match *status {
|
||||||
AuthStatus::Authorized(last_time) => {
|
AuthStatus::Authorized(last_time) => {
|
||||||
let current_time = Utc::now();
|
let current_time = Utc::now();
|
||||||
let days = (current_time - last_time).num_days();
|
let days = (current_time - last_time).num_days();
|
||||||
days < EXPIRED_DURATION_DAYS
|
days < EXPIRED_DURATION_DAYS
|
||||||
},
|
}
|
||||||
AuthStatus::NotAuthorized => {
|
AuthStatus::NotAuthorized => {
|
||||||
tracing::debug!("user logout already");
|
tracing::debug!("user logout already");
|
||||||
false
|
false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,9 @@ impl Claim {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn user_id(self) -> String { self.user_id }
|
pub fn user_id(self) -> String {
|
||||||
|
self.user_id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl From<Claim> for User {
|
// impl From<Claim> for User {
|
||||||
@ -48,7 +50,7 @@ impl Claim {
|
|||||||
pub struct Token(pub String);
|
pub struct Token(pub String);
|
||||||
impl Token {
|
impl Token {
|
||||||
pub fn create_token(user_id: &str) -> Result<Self, ServerError> {
|
pub fn create_token(user_id: &str) -> Result<Self, ServerError> {
|
||||||
let claims = Claim::with_user_id(&user_id);
|
let claims = Claim::with_user_id(user_id);
|
||||||
encode(
|
encode(
|
||||||
&Header::new(DEFAULT_ALGORITHM),
|
&Header::new(DEFAULT_ALGORITHM),
|
||||||
&claims,
|
&claims,
|
||||||
|
@ -7,6 +7,7 @@ pub struct UserTable {
|
|||||||
pub(crate) id: uuid::Uuid,
|
pub(crate) id: uuid::Uuid,
|
||||||
pub(crate) email: String,
|
pub(crate) email: String,
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) create_time: chrono::DateTime<Utc>,
|
pub(crate) create_time: chrono::DateTime<Utc>,
|
||||||
pub(crate) password: String,
|
pub(crate) password: String,
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
use actix_service::{Service, Transform};
|
use actix_service::{Service, Transform};
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
dev::{ServiceRequest, ServiceResponse},
|
dev::{ServiceRequest, ServiceResponse},
|
||||||
Error,
|
Error, HttpResponse, ResponseError,
|
||||||
HttpResponse,
|
|
||||||
ResponseError,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -34,7 +32,9 @@ where
|
|||||||
type InitError = ();
|
type InitError = ();
|
||||||
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
||||||
|
|
||||||
fn new_transform(&self, service: S) -> Self::Future { ok(AuthenticationMiddleware { service }) }
|
fn new_transform(&self, service: S) -> Self::Future {
|
||||||
|
ok(AuthenticationMiddleware { service })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub struct AuthenticationMiddleware<S> {
|
pub struct AuthenticationMiddleware<S> {
|
||||||
service: S,
|
service: S,
|
||||||
@ -51,7 +51,9 @@ where
|
|||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { self.service.poll_ready(cx) }
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
self.service.poll_ready(cx)
|
||||||
|
}
|
||||||
|
|
||||||
fn call(&self, req: ServiceRequest) -> Self::Future {
|
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||||
let mut authenticate_pass: bool = false;
|
let mut authenticate_pass: bool = false;
|
||||||
@ -77,7 +79,7 @@ where
|
|||||||
AUTHORIZED_USERS.store_auth(logged_user, true);
|
AUTHORIZED_USERS.store_auth(logged_user, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(e) => log::error!("{:?}", e),
|
Err(e) => log::error!("{:?}", e),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -101,7 +101,7 @@ fn default_color_style() -> Vec<u8> {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Serialize color style failed: {:?}", e);
|
log::error!("Serialize color style failed: {:?}", e);
|
||||||
vec![]
|
vec![]
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +121,7 @@ pub struct AppTable {
|
|||||||
pub(crate) last_view_id: String,
|
pub(crate) last_view_id: String,
|
||||||
pub(crate) modified_time: chrono::DateTime<Utc>,
|
pub(crate) modified_time: chrono::DateTime<Utc>,
|
||||||
pub(crate) create_time: chrono::DateTime<Utc>,
|
pub(crate) create_time: chrono::DateTime<Utc>,
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) user_id: String,
|
pub(crate) user_id: String,
|
||||||
}
|
}
|
||||||
impl std::convert::From<AppTable> for AppPB {
|
impl std::convert::From<AppTable> for AppPB {
|
||||||
|
@ -73,7 +73,7 @@ pub async fn update_handler(payload: Payload, pool: Data<PgPool>) -> Result<Http
|
|||||||
true => {
|
true => {
|
||||||
let color_bytes = params.get_color_style().write_to_bytes()?;
|
let color_bytes = params.get_color_style().write_to_bytes()?;
|
||||||
Some(color_bytes)
|
Some(color_bytes)
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let desc = match params.has_desc() {
|
let desc = match params.has_desc() {
|
||||||
|
@ -6,6 +6,7 @@ pub(crate) const TRASH_TABLE: &str = "trash_table";
|
|||||||
#[derive(Debug, Clone, sqlx::FromRow)]
|
#[derive(Debug, Clone, sqlx::FromRow)]
|
||||||
pub struct TrashTable {
|
pub struct TrashTable {
|
||||||
pub(crate) id: uuid::Uuid,
|
pub(crate) id: uuid::Uuid,
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) user_id: String,
|
pub(crate) user_id: String,
|
||||||
pub(crate) ty: i32,
|
pub(crate) ty: i32,
|
||||||
}
|
}
|
||||||
|
@ -117,13 +117,13 @@ async fn delete_trash_associate_targets(
|
|||||||
match TrashType::from_i32(ty) {
|
match TrashType::from_i32(ty) {
|
||||||
None => log::error!("Parser trash type with value: {} failed", ty),
|
None => log::error!("Parser trash type with value: {} failed", ty),
|
||||||
Some(ty) => match ty {
|
Some(ty) => match ty {
|
||||||
TrashType::Unknown => {},
|
TrashType::Unknown => {}
|
||||||
TrashType::View => {
|
TrashType::View => {
|
||||||
let _ = delete_view(transaction as &mut DBTransaction<'_>, kv_store, vec![id]).await;
|
let _ = delete_view(transaction as &mut DBTransaction<'_>, kv_store, vec![id]).await;
|
||||||
},
|
}
|
||||||
TrashType::App => {
|
TrashType::App => {
|
||||||
let _ = delete_app(transaction as &mut DBTransaction<'_>, id).await;
|
let _ = delete_app(transaction as &mut DBTransaction<'_>, id).await;
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,13 +164,13 @@ pub(crate) async fn read_trash(
|
|||||||
match TrashType::from_i32(table.ty) {
|
match TrashType::from_i32(table.ty) {
|
||||||
None => log::error!("Parser trash type with value: {} failed", table.ty),
|
None => log::error!("Parser trash type with value: {} failed", table.ty),
|
||||||
Some(ty) => match ty {
|
Some(ty) => match ty {
|
||||||
TrashType::Unknown => {},
|
TrashType::Unknown => {}
|
||||||
TrashType::View => {
|
TrashType::View => {
|
||||||
trash.push(read_view_table(table.id, transaction).await?.into());
|
trash.push(read_view_table(table.id, transaction).await?.into());
|
||||||
},
|
}
|
||||||
TrashType::App => {
|
TrashType::App => {
|
||||||
trash.push(read_app_table(table.id, transaction).await?.into());
|
trash.push(read_app_table(table.id, transaction).await?.into());
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ pub(crate) async fn read_view(
|
|||||||
|
|
||||||
let mut views = RepeatedViewPB::default();
|
let mut views = RepeatedViewPB::default();
|
||||||
views.set_items(
|
views.set_items(
|
||||||
read_view_belong_to_id(&table.id.to_string(), &user, transaction)
|
read_view_belong_to_id(&table.id.to_string(), user, transaction)
|
||||||
.await?
|
.await?
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
|
@ -2,11 +2,9 @@ use crate::{
|
|||||||
context::FlowyPersistence,
|
context::FlowyPersistence,
|
||||||
entities::logged_user::LoggedUser,
|
entities::logged_user::LoggedUser,
|
||||||
services::core::view::{
|
services::core::view::{
|
||||||
create_view,
|
create_view, delete_view,
|
||||||
delete_view,
|
|
||||||
persistence::{check_view_id, check_view_ids},
|
persistence::{check_view_id, check_view_ids},
|
||||||
read_view,
|
read_view, update_view,
|
||||||
update_view,
|
|
||||||
},
|
},
|
||||||
util::serde_ext::parse_from_payload,
|
util::serde_ext::parse_from_payload,
|
||||||
};
|
};
|
||||||
@ -22,10 +20,8 @@ use backend_service::{
|
|||||||
use flowy_core_data_model::{
|
use flowy_core_data_model::{
|
||||||
parser::view::{ViewDesc, ViewName, ViewThumbnail},
|
parser::view::{ViewDesc, ViewName, ViewThumbnail},
|
||||||
protobuf::{
|
protobuf::{
|
||||||
CreateViewParams as CreateViewParamsPB,
|
CreateViewParams as CreateViewParamsPB, QueryViewRequest as QueryViewRequestPB,
|
||||||
QueryViewRequest as QueryViewRequestPB,
|
UpdateViewParams as UpdateViewParamsPB, ViewId as ViewIdPB,
|
||||||
UpdateViewParams as UpdateViewParamsPB,
|
|
||||||
ViewId as ViewIdPB,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
entities::logged_user::LoggedUser,
|
entities::logged_user::LoggedUser,
|
||||||
services::core::workspace::{
|
services::core::workspace::{
|
||||||
create_workspace,
|
create_workspace, delete_workspace, persistence::check_workspace_id, read_workspaces, update_workspace,
|
||||||
delete_workspace,
|
|
||||||
persistence::check_workspace_id,
|
|
||||||
read_workspaces,
|
|
||||||
update_workspace,
|
|
||||||
},
|
},
|
||||||
util::serde_ext::parse_from_payload,
|
util::serde_ext::parse_from_payload,
|
||||||
};
|
};
|
||||||
@ -21,8 +17,7 @@ use backend_service::{
|
|||||||
use flowy_core_data_model::{
|
use flowy_core_data_model::{
|
||||||
parser::workspace::{WorkspaceDesc, WorkspaceName},
|
parser::workspace::{WorkspaceDesc, WorkspaceName},
|
||||||
protobuf::{
|
protobuf::{
|
||||||
CreateWorkspaceParams as CreateWorkspaceParamsPB,
|
CreateWorkspaceParams as CreateWorkspaceParamsPB, UpdateWorkspaceParams as UpdateWorkspaceParamsPB,
|
||||||
UpdateWorkspaceParams as UpdateWorkspaceParamsPB,
|
|
||||||
WorkspaceId as WorkspaceIdPB,
|
WorkspaceId as WorkspaceIdPB,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -110,7 +105,7 @@ pub async fn update_handler(
|
|||||||
.map_err(invalid_params)?
|
.map_err(invalid_params)?
|
||||||
.0;
|
.0;
|
||||||
Some(name)
|
Some(name)
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let desc = match params.has_desc() {
|
let desc = match params.has_desc() {
|
||||||
@ -120,7 +115,7 @@ pub async fn update_handler(
|
|||||||
.map_err(invalid_params)?
|
.map_err(invalid_params)?
|
||||||
.0;
|
.0;
|
||||||
Some(desc)
|
Some(desc)
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut transaction = pool
|
let mut transaction = pool
|
||||||
|
@ -7,11 +7,7 @@ use backend_service::errors::{internal_error, ServerError};
|
|||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use flowy_collaboration::{
|
use flowy_collaboration::{
|
||||||
protobuf::{
|
protobuf::{
|
||||||
CreateDocParams,
|
CreateDocParams, DocumentId, DocumentInfo, RepeatedRevision as RepeatedRevisionPB, ResetDocumentParams,
|
||||||
DocumentId,
|
|
||||||
DocumentInfo,
|
|
||||||
RepeatedRevision as RepeatedRevisionPB,
|
|
||||||
ResetDocumentParams,
|
|
||||||
Revision as RevisionPB,
|
Revision as RevisionPB,
|
||||||
},
|
},
|
||||||
sync::ServerDocumentManager,
|
sync::ServerDocumentManager,
|
||||||
@ -71,15 +67,21 @@ pub struct DocumentKVPersistence {
|
|||||||
impl std::ops::Deref for DocumentKVPersistence {
|
impl std::ops::Deref for DocumentKVPersistence {
|
||||||
type Target = Arc<KVStore>;
|
type Target = Arc<KVStore>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target { &self.inner }
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::DerefMut for DocumentKVPersistence {
|
impl std::ops::DerefMut for DocumentKVPersistence {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.inner
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DocumentKVPersistence {
|
impl DocumentKVPersistence {
|
||||||
pub(crate) fn new(kv_store: Arc<KVStore>) -> Self { DocumentKVPersistence { inner: kv_store } }
|
pub(crate) fn new(kv_store: Arc<KVStore>) -> Self {
|
||||||
|
DocumentKVPersistence { inner: kv_store }
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn batch_set_revision(&self, revisions: Vec<RevisionPB>) -> Result<(), ServerError> {
|
pub(crate) async fn batch_set_revision(&self, revisions: Vec<RevisionPB>) -> Result<(), ServerError> {
|
||||||
let items = revisions_to_key_value_items(revisions)?;
|
let items = revisions_to_key_value_items(revisions)?;
|
||||||
@ -109,7 +111,7 @@ impl DocumentKVPersistence {
|
|||||||
self.inner
|
self.inner
|
||||||
.transaction(|mut t| Box::pin(async move { t.batch_get_start_with(&doc_id).await }))
|
.transaction(|mut t| Box::pin(async move { t.batch_get_start_with(&doc_id).await }))
|
||||||
.await?
|
.await?
|
||||||
},
|
}
|
||||||
Some(rev_ids) => {
|
Some(rev_ids) => {
|
||||||
let keys = rev_ids
|
let keys = rev_ids
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -119,7 +121,7 @@ impl DocumentKVPersistence {
|
|||||||
self.inner
|
self.inner
|
||||||
.transaction(|mut t| Box::pin(async move { t.batch_get(keys).await }))
|
.transaction(|mut t| Box::pin(async move { t.batch_get(keys).await }))
|
||||||
.await?
|
.await?
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(key_value_items_to_revisions(items))
|
Ok(key_value_items_to_revisions(items))
|
||||||
@ -136,7 +138,7 @@ impl DocumentKVPersistence {
|
|||||||
self.inner
|
self.inner
|
||||||
.transaction(|mut t| Box::pin(async move { t.batch_delete_key_start_with(&doc_id).await }))
|
.transaction(|mut t| Box::pin(async move { t.batch_delete_key_start_with(&doc_id).await }))
|
||||||
.await
|
.await
|
||||||
},
|
}
|
||||||
Some(rev_ids) => {
|
Some(rev_ids) => {
|
||||||
let keys = rev_ids
|
let keys = rev_ids
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -146,7 +148,7 @@ impl DocumentKVPersistence {
|
|||||||
self.inner
|
self.inner
|
||||||
.transaction(|mut t| Box::pin(async move { t.batch_delete(keys).await }))
|
.transaction(|mut t| Box::pin(async move { t.batch_delete(keys).await }))
|
||||||
.await
|
.await
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,7 +183,9 @@ fn key_value_items_to_revisions(items: Vec<KeyValue>) -> RepeatedRevisionPB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn make_revision_key(doc_id: &str, rev_id: i64) -> String { format!("{}:{}", doc_id, rev_id) }
|
fn make_revision_key(doc_id: &str, rev_id: i64) -> String {
|
||||||
|
format!("{}:{}", doc_id, rev_id)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn make_doc_from_revisions(doc_id: &str, mut revisions: RepeatedRevisionPB) -> Result<DocumentInfo, ServerError> {
|
fn make_doc_from_revisions(doc_id: &str, mut revisions: RepeatedRevisionPB) -> Result<DocumentInfo, ServerError> {
|
||||||
|
@ -10,9 +10,7 @@ use actix_web::{
|
|||||||
use backend_service::{errors::ServerError, response::FlowyResponse};
|
use backend_service::{errors::ServerError, response::FlowyResponse};
|
||||||
use flowy_collaboration::{
|
use flowy_collaboration::{
|
||||||
protobuf::{
|
protobuf::{
|
||||||
CreateDocParams as CreateDocParamsPB,
|
CreateDocParams as CreateDocParamsPB, DocumentId as DocumentIdPB, ResetDocumentParams as ResetDocumentParamsPB,
|
||||||
DocumentId as DocumentIdPB,
|
|
||||||
ResetDocumentParams as ResetDocumentParamsPB,
|
|
||||||
},
|
},
|
||||||
sync::ServerDocumentManager,
|
sync::ServerDocumentManager,
|
||||||
};
|
};
|
||||||
|
@ -9,8 +9,7 @@ use backend_service::errors::{internal_error, Result, ServerError};
|
|||||||
|
|
||||||
use flowy_collaboration::{
|
use flowy_collaboration::{
|
||||||
protobuf::{
|
protobuf::{
|
||||||
DocumentClientWSData as DocumentClientWSDataPB,
|
DocumentClientWSData as DocumentClientWSDataPB, DocumentClientWSDataType as DocumentClientWSDataTypePB,
|
||||||
DocumentClientWSDataType as DocumentClientWSDataTypePB,
|
|
||||||
Revision as RevisionPB,
|
Revision as RevisionPB,
|
||||||
},
|
},
|
||||||
sync::{RevisionUser, ServerDocumentManager, SyncResponse},
|
sync::{RevisionUser, ServerDocumentManager, SyncResponse},
|
||||||
@ -66,7 +65,7 @@ impl DocumentWebSocketActor {
|
|||||||
ret,
|
ret,
|
||||||
} => {
|
} => {
|
||||||
let _ = ret.send(self.handle_client_data(client_data, persistence).await);
|
let _ = ret.send(self.handle_client_data(client_data, persistence).await);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,14 +95,14 @@ impl DocumentWebSocketActor {
|
|||||||
.handle_client_revisions(user, document_client_data)
|
.handle_client_revisions(user, document_client_data)
|
||||||
.await
|
.await
|
||||||
.map_err(internal_error)?;
|
.map_err(internal_error)?;
|
||||||
},
|
}
|
||||||
DocumentClientWSDataTypePB::ClientPing => {
|
DocumentClientWSDataTypePB::ClientPing => {
|
||||||
let _ = self
|
let _ = self
|
||||||
.doc_manager
|
.doc_manager
|
||||||
.handle_client_ping(user, document_client_data)
|
.handle_client_ping(user, document_client_data)
|
||||||
.await
|
.await
|
||||||
.map_err(internal_error)?;
|
.map_err(internal_error)?;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -135,37 +134,39 @@ impl std::fmt::Debug for ServerDocUser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RevisionUser for ServerDocUser {
|
impl RevisionUser for ServerDocUser {
|
||||||
fn user_id(&self) -> String { self.user.id().to_string() }
|
fn user_id(&self) -> String {
|
||||||
|
self.user.id().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
fn receive(&self, resp: SyncResponse) {
|
fn receive(&self, resp: SyncResponse) {
|
||||||
let result = match resp {
|
let result = match resp {
|
||||||
SyncResponse::Pull(data) => {
|
SyncResponse::Pull(data) => {
|
||||||
let msg: WebSocketMessage = data.into();
|
let msg: WebSocketMessage = data.into();
|
||||||
self.socket.try_send(msg).map_err(internal_error)
|
self.socket.try_send(msg).map_err(internal_error)
|
||||||
},
|
}
|
||||||
SyncResponse::Push(data) => {
|
SyncResponse::Push(data) => {
|
||||||
let msg: WebSocketMessage = data.into();
|
let msg: WebSocketMessage = data.into();
|
||||||
self.socket.try_send(msg).map_err(internal_error)
|
self.socket.try_send(msg).map_err(internal_error)
|
||||||
},
|
}
|
||||||
SyncResponse::Ack(data) => {
|
SyncResponse::Ack(data) => {
|
||||||
let msg: WebSocketMessage = data.into();
|
let msg: WebSocketMessage = data.into();
|
||||||
self.socket.try_send(msg).map_err(internal_error)
|
self.socket.try_send(msg).map_err(internal_error)
|
||||||
},
|
}
|
||||||
SyncResponse::NewRevision(mut repeated_revision) => {
|
SyncResponse::NewRevision(mut repeated_revision) => {
|
||||||
let kv_store = self.persistence.kv_store();
|
let kv_store = self.persistence.kv_store();
|
||||||
tokio::task::spawn(async move {
|
tokio::task::spawn(async move {
|
||||||
let revisions = repeated_revision.take_items().into();
|
let revisions = repeated_revision.take_items().into();
|
||||||
match kv_store.batch_set_revision(revisions).await {
|
match kv_store.batch_set_revision(revisions).await {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => log::error!("{}", e),
|
Err(e) => log::error!("{}", e),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => log::error!("[ServerDocUser]: {}", e),
|
Err(e) => log::error!("[ServerDocUser]: {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,7 @@ use flowy_collaboration::{
|
|||||||
entities::doc::DocumentInfo,
|
entities::doc::DocumentInfo,
|
||||||
errors::CollaborateError,
|
errors::CollaborateError,
|
||||||
protobuf::{
|
protobuf::{
|
||||||
CreateDocParams as CreateDocParamsPB,
|
CreateDocParams as CreateDocParamsPB, DocumentId, RepeatedRevision as RepeatedRevisionPB,
|
||||||
DocumentId,
|
|
||||||
RepeatedRevision as RepeatedRevisionPB,
|
|
||||||
Revision as RevisionPB,
|
Revision as RevisionPB,
|
||||||
},
|
},
|
||||||
sync::{DocumentPersistence, ServerDocumentManager},
|
sync::{DocumentPersistence, ServerDocumentManager},
|
||||||
@ -65,11 +63,11 @@ impl WebSocketReceiver for DocumentWebSocketReceiver {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match sender.send(msg).await {
|
match sender.send(msg).await {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => log::error!("{}", e),
|
Err(e) => log::error!("{}", e),
|
||||||
}
|
}
|
||||||
match rx.await {
|
match rx.await {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => log::error!("{:?}", e),
|
Err(e) => log::error!("{:?}", e),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -78,7 +76,9 @@ impl WebSocketReceiver for DocumentWebSocketReceiver {
|
|||||||
|
|
||||||
pub struct DocumentPersistenceImpl(pub Arc<FlowyPersistence>);
|
pub struct DocumentPersistenceImpl(pub Arc<FlowyPersistence>);
|
||||||
impl Debug for DocumentPersistenceImpl {
|
impl Debug for DocumentPersistenceImpl {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str("DocumentPersistenceImpl") }
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str("DocumentPersistenceImpl")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DocumentPersistence for DocumentPersistenceImpl {
|
impl DocumentPersistence for DocumentPersistenceImpl {
|
||||||
|
@ -11,11 +11,7 @@ use lib_infra::future::BoxResultFuture;
|
|||||||
use sql_builder::SqlBuilder as RawSqlBuilder;
|
use sql_builder::SqlBuilder as RawSqlBuilder;
|
||||||
use sqlx::{
|
use sqlx::{
|
||||||
postgres::{PgArguments, PgRow},
|
postgres::{PgArguments, PgRow},
|
||||||
Arguments,
|
Arguments, Error, PgPool, Postgres, Row,
|
||||||
Error,
|
|
||||||
PgPool,
|
|
||||||
Postgres,
|
|
||||||
Row,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const KV_TABLE: &str = "kv_table";
|
const KV_TABLE: &str = "kv_table";
|
||||||
@ -208,6 +204,7 @@ fn rows_to_key_values(rows: Vec<PgRow>) -> Vec<KeyValue> {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, sqlx::FromRow)]
|
#[derive(Debug, Clone, sqlx::FromRow)]
|
||||||
struct KVTable {
|
struct KVTable {
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) id: String,
|
pub(crate) id: String,
|
||||||
pub(crate) blob: Vec<u8>,
|
pub(crate) blob: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
@ -18,12 +18,8 @@ use chrono::Utc;
|
|||||||
use flowy_user_data_model::{
|
use flowy_user_data_model::{
|
||||||
parser::{UserEmail, UserName, UserPassword},
|
parser::{UserEmail, UserName, UserPassword},
|
||||||
protobuf::{
|
protobuf::{
|
||||||
SignInParams as SignInParamsPB,
|
SignInParams as SignInParamsPB, SignInResponse as SignInResponsePB, SignUpParams as SignUpParamsPB,
|
||||||
SignInResponse as SignInResponsePB,
|
SignUpResponse as SignUpResponsePB, UpdateUserParams as UpdateUserParamsPB, UserProfile as UserProfilePB,
|
||||||
SignUpParams as SignUpParamsPB,
|
|
||||||
SignUpResponse as SignUpResponsePB,
|
|
||||||
UpdateUserParams as UpdateUserParamsPB,
|
|
||||||
UserProfile as UserProfilePB,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use sqlx::{PgPool, Postgres};
|
use sqlx::{PgPool, Postgres};
|
||||||
@ -150,7 +146,7 @@ pub(crate) async fn set_user_profile(
|
|||||||
let password = UserPassword::parse(params.get_password().to_owned()).map_err(invalid_params)?;
|
let password = UserPassword::parse(params.get_password().to_owned()).map_err(invalid_params)?;
|
||||||
let password = hash_password(password.as_ref())?;
|
let password = hash_password(password.as_ref())?;
|
||||||
Some(password)
|
Some(password)
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (sql, args) = SqlBuilder::update("user_table")
|
let (sql, args) = SqlBuilder::update("user_table")
|
||||||
@ -200,7 +196,7 @@ async fn check_user_password(
|
|||||||
.await
|
.await
|
||||||
.map_err(|err| ServerError::internal().context(err))?;
|
.map_err(|err| ServerError::internal().context(err))?;
|
||||||
|
|
||||||
match verify_password(&password, &user.password) {
|
match verify_password(password, &user.password) {
|
||||||
Ok(true) => Ok(user),
|
Ok(true) => Ok(user),
|
||||||
_ => Err(ServerError::password_not_match()),
|
_ => Err(ServerError::password_not_match()),
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,11 @@ use crate::{
|
|||||||
use actix_identity::Identity;
|
use actix_identity::Identity;
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
web::{Data, Payload},
|
web::{Data, Payload},
|
||||||
HttpRequest,
|
HttpRequest, HttpResponse,
|
||||||
HttpResponse,
|
|
||||||
};
|
};
|
||||||
use backend_service::{errors::ServerError, response::FlowyResponse};
|
use backend_service::{errors::ServerError, response::FlowyResponse};
|
||||||
use flowy_user_data_model::protobuf::{
|
use flowy_user_data_model::protobuf::{
|
||||||
SignInParams as SignInParamsPB,
|
SignInParams as SignInParamsPB, SignUpParams as SignUpParamsPB, UpdateUserParams as UpdateUserParamsPB,
|
||||||
SignUpParams as SignUpParamsPB,
|
|
||||||
UpdateUserParams as UpdateUserParamsPB,
|
|
||||||
};
|
};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
|
@ -10,13 +10,15 @@ pub type Socket = Recipient<WebSocketMessage>;
|
|||||||
pub struct SessionId(pub String);
|
pub struct SessionId(pub String);
|
||||||
|
|
||||||
impl<T: AsRef<str>> std::convert::From<T> for SessionId {
|
impl<T: AsRef<str>> std::convert::From<T> for SessionId {
|
||||||
fn from(s: T) -> Self { SessionId(s.as_ref().to_owned()) }
|
fn from(s: T) -> Self {
|
||||||
|
SessionId(s.as_ref().to_owned())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for SessionId {
|
impl std::fmt::Display for SessionId {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
let desc = &self.0.to_string();
|
let desc = &self.0.to_string();
|
||||||
f.write_str(&desc)
|
f.write_str(desc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,9 @@ pub struct WebSocketMessage(pub Bytes);
|
|||||||
impl std::ops::Deref for WebSocketMessage {
|
impl std::ops::Deref for WebSocketMessage {
|
||||||
type Target = Bytes;
|
type Target = Bytes;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target { &self.0 }
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<DocumentClientWSData> for WebSocketMessage {
|
impl std::convert::From<DocumentClientWSData> for WebSocketMessage {
|
||||||
|
@ -6,9 +6,7 @@ use actix::Addr;
|
|||||||
use actix_web::{
|
use actix_web::{
|
||||||
get,
|
get,
|
||||||
web::{Data, Path, Payload},
|
web::{Data, Path, Payload},
|
||||||
Error,
|
Error, HttpRequest, HttpResponse,
|
||||||
HttpRequest,
|
|
||||||
HttpResponse,
|
|
||||||
};
|
};
|
||||||
use actix_web_actors::ws;
|
use actix_web_actors::ws;
|
||||||
|
|
||||||
|
@ -3,8 +3,7 @@ use crate::{
|
|||||||
entities::logged_user::LoggedUser,
|
entities::logged_user::LoggedUser,
|
||||||
services::web_socket::{
|
services::web_socket::{
|
||||||
entities::{Connect, Disconnect, Socket},
|
entities::{Connect, Disconnect, Socket},
|
||||||
WSServer,
|
WSServer, WebSocketMessage,
|
||||||
WebSocketMessage,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use actix::*;
|
use actix::*;
|
||||||
@ -18,22 +17,23 @@ pub trait WebSocketReceiver: Send + Sync {
|
|||||||
fn receive(&self, data: WSClientData);
|
fn receive(&self, data: WSClientData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct WebSocketReceivers {
|
pub struct WebSocketReceivers {
|
||||||
inner: HashMap<WSModule, Arc<dyn WebSocketReceiver>>,
|
inner: HashMap<WSModule, Arc<dyn WebSocketReceiver>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::default::Default for WebSocketReceivers {
|
|
||||||
fn default() -> Self { Self { inner: HashMap::new() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WebSocketReceivers {
|
impl WebSocketReceivers {
|
||||||
pub fn new() -> Self { WebSocketReceivers::default() }
|
pub fn new() -> Self {
|
||||||
|
WebSocketReceivers::default()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, source: WSModule, receiver: Arc<dyn WebSocketReceiver>) {
|
pub fn set(&mut self, source: WSModule, receiver: Arc<dyn WebSocketReceiver>) {
|
||||||
self.inner.insert(source, receiver);
|
self.inner.insert(source, receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, source: &WSModule) -> Option<Arc<dyn WebSocketReceiver>> { self.inner.get(source).cloned() }
|
pub fn get(&self, source: &WSModule) -> Option<Arc<dyn WebSocketReceiver>> {
|
||||||
|
self.inner.get(source).cloned()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -42,9 +42,13 @@ pub struct WSUser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WSUser {
|
impl WSUser {
|
||||||
pub fn new(inner: LoggedUser) -> Self { Self { inner } }
|
pub fn new(inner: LoggedUser) -> Self {
|
||||||
|
Self { inner }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> &str { &self.inner.user_id }
|
pub fn id(&self) -> &str {
|
||||||
|
&self.inner.user_id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WSClientData {
|
pub struct WSClientData {
|
||||||
@ -89,7 +93,7 @@ impl WSClient {
|
|||||||
match self.ws_receivers.get(&message.module) {
|
match self.ws_receivers.get(&message.module) {
|
||||||
None => {
|
None => {
|
||||||
log::error!("Can't find the receiver for {:?}", message.module);
|
log::error!("Can't find the receiver for {:?}", message.module);
|
||||||
},
|
}
|
||||||
Some(handler) => {
|
Some(handler) => {
|
||||||
let client_data = WSClientData {
|
let client_data = WSClientData {
|
||||||
user: self.user.clone(),
|
user: self.user.clone(),
|
||||||
@ -97,7 +101,7 @@ impl WSClient {
|
|||||||
data: Bytes::from(message.data),
|
data: Bytes::from(message.data),
|
||||||
};
|
};
|
||||||
handler.receive(client_data);
|
handler.receive(client_data);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,28 +112,28 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WSClient {
|
|||||||
Ok(ws::Message::Ping(msg)) => {
|
Ok(ws::Message::Ping(msg)) => {
|
||||||
self.hb = Instant::now();
|
self.hb = Instant::now();
|
||||||
ctx.pong(&msg);
|
ctx.pong(&msg);
|
||||||
},
|
}
|
||||||
Ok(ws::Message::Pong(_msg)) => {
|
Ok(ws::Message::Pong(_msg)) => {
|
||||||
// tracing::debug!("Receive {} pong {:?}", &self.session_id, &msg);
|
// tracing::debug!("Receive {} pong {:?}", &self.session_id, &msg);
|
||||||
self.hb = Instant::now();
|
self.hb = Instant::now();
|
||||||
},
|
}
|
||||||
Ok(ws::Message::Binary(bytes)) => {
|
Ok(ws::Message::Binary(bytes)) => {
|
||||||
let socket = ctx.address().recipient();
|
let socket = ctx.address().recipient();
|
||||||
self.handle_binary_message(bytes, socket);
|
self.handle_binary_message(bytes, socket);
|
||||||
},
|
}
|
||||||
Ok(Text(_)) => {
|
Ok(Text(_)) => {
|
||||||
log::warn!("Receive unexpected text message");
|
log::warn!("Receive unexpected text message");
|
||||||
},
|
}
|
||||||
Ok(ws::Message::Close(reason)) => {
|
Ok(ws::Message::Close(reason)) => {
|
||||||
ctx.close(reason);
|
ctx.close(reason);
|
||||||
ctx.stop();
|
ctx.stop();
|
||||||
},
|
}
|
||||||
Ok(ws::Message::Continuation(_)) => {},
|
Ok(ws::Message::Continuation(_)) => {}
|
||||||
Ok(ws::Message::Nop) => {},
|
Ok(ws::Message::Nop) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("[{}]: WebSocketStream protocol error {:?}", self.user.id(), e);
|
log::error!("[{}]: WebSocketStream protocol error {:?}", self.user.id(), e);
|
||||||
ctx.stop();
|
ctx.stop();
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,7 +141,9 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WSClient {
|
|||||||
impl Handler<WebSocketMessage> for WSClient {
|
impl Handler<WebSocketMessage> for WSClient {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
|
|
||||||
fn handle(&mut self, msg: WebSocketMessage, ctx: &mut Self::Context) { ctx.binary(msg.0); }
|
fn handle(&mut self, msg: WebSocketMessage, ctx: &mut Self::Context) {
|
||||||
|
ctx.binary(msg.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Actor for WSClient {
|
impl Actor for WSClient {
|
||||||
|
@ -18,9 +18,13 @@ impl std::default::Default for WSServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl WSServer {
|
impl WSServer {
|
||||||
pub fn new() -> Self { WSServer::default() }
|
pub fn new() -> Self {
|
||||||
|
WSServer::default()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn send(&self, _msg: WebSocketMessage) { unimplemented!() }
|
pub fn send(&self, _msg: WebSocketMessage) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Actor for WSServer {
|
impl Actor for WSServer {
|
||||||
@ -49,7 +53,9 @@ impl Handler<Disconnect> for WSServer {
|
|||||||
impl Handler<WebSocketMessage> for WSServer {
|
impl Handler<WebSocketMessage> for WSServer {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
|
|
||||||
fn handle(&mut self, _msg: WebSocketMessage, _ctx: &mut Context<Self>) -> Self::Result { unimplemented!() }
|
fn handle(&mut self, _msg: WebSocketMessage, _ctx: &mut Context<Self>) -> Self::Result {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl actix::Supervised for WSServer {
|
impl actix::Supervised for WSServer {
|
||||||
|
@ -22,7 +22,7 @@ pub fn md5<T: AsRef<[u8]>>(data: T) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_from_bytes<T: Message>(bytes: &[u8]) -> Result<T, ServerError> {
|
pub fn parse_from_bytes<T: Message>(bytes: &[u8]) -> Result<T, ServerError> {
|
||||||
let result: ProtobufResult<T> = Message::parse_from_bytes(&bytes);
|
let result: ProtobufResult<T> = Message::parse_from_bytes(bytes);
|
||||||
match result {
|
match result {
|
||||||
Ok(data) => Ok(data),
|
Ok(data) => Ok(data),
|
||||||
Err(e) => Err(e.into()),
|
Err(e) => Err(e.into()),
|
||||||
|
@ -117,7 +117,7 @@ impl SqlBuilder {
|
|||||||
|
|
||||||
let sql = inner.sql()?;
|
let sql = inner.sql()?;
|
||||||
Ok((sql, self.fields_args))
|
Ok((sql, self.fields_args))
|
||||||
},
|
}
|
||||||
BuilderType::Select => {
|
BuilderType::Select => {
|
||||||
let mut inner = InnerBuilder::select_from(&self.table);
|
let mut inner = InnerBuilder::select_from(&self.table);
|
||||||
self.fields.into_iter().for_each(|field| {
|
self.fields.into_iter().for_each(|field| {
|
||||||
@ -130,7 +130,7 @@ impl SqlBuilder {
|
|||||||
|
|
||||||
let sql = inner.sql()?;
|
let sql = inner.sql()?;
|
||||||
Ok((sql, self.fields_args))
|
Ok((sql, self.fields_args))
|
||||||
},
|
}
|
||||||
BuilderType::Update => {
|
BuilderType::Update => {
|
||||||
let mut inner = InnerBuilder::update_table(&self.table);
|
let mut inner = InnerBuilder::update_table(&self.table);
|
||||||
let field_len = self.fields.len();
|
let field_len = self.fields.len();
|
||||||
@ -145,7 +145,7 @@ impl SqlBuilder {
|
|||||||
|
|
||||||
let sql = inner.sql()?;
|
let sql = inner.sql()?;
|
||||||
Ok((sql, self.fields_args))
|
Ok((sql, self.fields_args))
|
||||||
},
|
}
|
||||||
BuilderType::Delete => {
|
BuilderType::Delete => {
|
||||||
let mut inner = InnerBuilder::delete_from(&self.table);
|
let mut inner = InnerBuilder::delete_from(&self.table);
|
||||||
self.filters.into_iter().enumerate().for_each(|(index, filter)| {
|
self.filters.into_iter().enumerate().for_each(|(index, filter)| {
|
||||||
@ -153,7 +153,7 @@ impl SqlBuilder {
|
|||||||
});
|
});
|
||||||
let sql = inner.sql()?;
|
let sql = inner.sql()?;
|
||||||
Ok((sql, self.fields_args))
|
Ok((sql, self.fields_args))
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,9 @@ use backend_service::errors::{ErrorCode, ServerError};
|
|||||||
use bcrypt::{hash, verify, DEFAULT_COST};
|
use bcrypt::{hash, verify, DEFAULT_COST};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn uuid() -> String { uuid::Uuid::new_v4().to_string() }
|
pub fn uuid() -> String {
|
||||||
|
uuid::Uuid::new_v4().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn hash_password(plain: &str) -> Result<String, ServerError> {
|
pub fn hash_password(plain: &str) -> Result<String, ServerError> {
|
||||||
let hashing_cost = std::env::var("HASH_COST")
|
let hashing_cost = std::env::var("HASH_COST")
|
||||||
|
@ -76,10 +76,10 @@ async fn user_update_password() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match server.sign_in(sign_in_params).await {
|
match server.sign_in(sign_in_params).await {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
assert_eq!(e.code, ErrorCode::PasswordNotMatch);
|
assert_eq!(e.code, ErrorCode::PasswordNotMatch);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,9 +45,13 @@ impl TestUserServer {
|
|||||||
let _ = user_sign_out_request(self.user_token(), &url).await.unwrap();
|
let _ = user_sign_out_request(self.user_token(), &url).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn user_token(&self) -> &str { self.user_token.as_ref().expect("must call register_user first ") }
|
pub fn user_token(&self) -> &str {
|
||||||
|
self.user_token.as_ref().expect("must call register_user first ")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn user_id(&self) -> &str { self.user_id.as_ref().expect("must call register_user first ") }
|
pub fn user_id(&self) -> &str {
|
||||||
|
self.user_id.as_ref().expect("must call register_user first ")
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_user_profile(&self) -> UserProfile {
|
pub async fn get_user_profile(&self) -> UserProfile {
|
||||||
let url = format!("{}/api/user", self.http_addr());
|
let url = format!("{}/api/user", self.http_addr());
|
||||||
@ -178,7 +182,9 @@ impl TestUserServer {
|
|||||||
response
|
response
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn http_addr(&self) -> String { self.inner.client_server_config.base_url() }
|
pub fn http_addr(&self) -> String {
|
||||||
|
self.inner.client_server_config.base_url()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn ws_addr(&self) -> String {
|
pub fn ws_addr(&self) -> String {
|
||||||
format!(
|
format!(
|
||||||
@ -336,7 +342,9 @@ impl WorkspaceTest {
|
|||||||
Self { server, workspace }
|
Self { server, workspace }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_app(&self) -> App { create_test_app(&self.server, &self.workspace.id).await }
|
pub async fn create_app(&self) -> App {
|
||||||
|
create_test_app(&self.server, &self.workspace.id).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AppTest {
|
pub struct AppTest {
|
||||||
|
@ -34,8 +34,8 @@ yay -S curl base-devel sqlite openssl clang cmake ninja pkg-config gtk3 unzip
|
|||||||
```shell
|
```shell
|
||||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||||
source $HOME/.cargo/env
|
source $HOME/.cargo/env
|
||||||
rustup toolchain install nightly
|
rustup toolchain install stable
|
||||||
rustup default nightly
|
rustup default stable
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Install flutter according to https://docs.flutter.dev/get-started/install/linux
|
3. Install flutter according to https://docs.flutter.dev/get-started/install/linux
|
||||||
|
@ -32,7 +32,7 @@ flutter doctor
|
|||||||
```shell
|
```shell
|
||||||
# Download rustup.exe from https://win.rustup.rs/x86_64
|
# Download rustup.exe from https://win.rustup.rs/x86_64
|
||||||
# Call rustup.exe from powershell or cmd
|
# Call rustup.exe from powershell or cmd
|
||||||
.\rustup-init.exe --default-toolchain nightly --default-host x86_64-pc-windows-msvc -y
|
.\rustup-init.exe --default-toolchain stable --default-host x86_64-pc-windows-msvc -y
|
||||||
# Note: you probably need to re-open termial to get cargo command be available in PATH var
|
# Note: you probably need to re-open termial to get cargo command be available in PATH var
|
||||||
```
|
```
|
||||||
5. Install cargo make
|
5. Install cargo make
|
||||||
|
@ -9,4 +9,4 @@ install_cargo_make:
|
|||||||
|
|
||||||
install_rust:
|
install_rust:
|
||||||
brew bundle
|
brew bundle
|
||||||
rustup-init -y --default-toolchain=nightly
|
rustup-init -y --default-toolchain=stable
|
||||||
|
2
frontend/app_flowy/.gitignore
vendored
2
frontend/app_flowy/.gitignore
vendored
@ -59,4 +59,4 @@ windows/flutter/dart_ffi/
|
|||||||
**/**/*.lib
|
**/**/*.lib
|
||||||
**/**/*.dll
|
**/**/*.dll
|
||||||
**/**/*.so
|
**/**/*.so
|
||||||
**/Brewfile.lock.json
|
**/**/Brewfile.lock.json
|
@ -18,7 +18,9 @@ lazy_static! {
|
|||||||
static ref FLOWY_SDK: RwLock<Option<Arc<FlowySDK>>> = RwLock::new(None);
|
static ref FLOWY_SDK: RwLock<Option<Arc<FlowySDK>>> = RwLock::new(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch() -> Arc<EventDispatcher> { FLOWY_SDK.read().as_ref().unwrap().dispatcher() }
|
fn dispatch() -> Arc<EventDispatcher> {
|
||||||
|
FLOWY_SDK.read().as_ref().unwrap().dispatcher()
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn init_sdk(path: *mut c_char) -> i64 {
|
pub extern "C" fn init_sdk(path: *mut c_char) -> i64 {
|
||||||
@ -85,13 +87,13 @@ async fn post_to_flutter(response: EventResponse, port: i64) {
|
|||||||
{
|
{
|
||||||
Ok(_success) => {
|
Ok(_success) => {
|
||||||
log::trace!("[FFI]: Post data to dart success");
|
log::trace!("[FFI]: Post data to dart success");
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if let Some(msg) = e.downcast_ref::<&str>() {
|
if let Some(msg) = e.downcast_ref::<&str>() {
|
||||||
log::error!("[FFI]: {:?}", msg);
|
log::error!("[FFI]: {:?}", msg);
|
||||||
} else {
|
} else {
|
||||||
log::error!("[FFI]: allo_isolate post panic");
|
log::error!("[FFI]: allo_isolate post panic");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,5 +22,7 @@ impl FFIRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<FFIRequest> for ModuleRequest {
|
impl std::convert::From<FFIRequest> for ModuleRequest {
|
||||||
fn from(ffi_request: FFIRequest) -> Self { ModuleRequest::new(ffi_request.event).payload(ffi_request.payload) }
|
fn from(ffi_request: FFIRequest) -> Self {
|
||||||
|
ModuleRequest::new(ffi_request.event).payload(ffi_request.payload)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,15 @@ use lib_dispatch::prelude::{EventResponse, Payload, StatusCode};
|
|||||||
|
|
||||||
#[derive(ProtoBuf_Enum, Clone, Copy)]
|
#[derive(ProtoBuf_Enum, Clone, Copy)]
|
||||||
pub enum FFIStatusCode {
|
pub enum FFIStatusCode {
|
||||||
Ok = 0,
|
Ok = 0,
|
||||||
Err = 1,
|
Err = 1,
|
||||||
Internal = 2,
|
Internal = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::default::Default for FFIStatusCode {
|
impl std::default::Default for FFIStatusCode {
|
||||||
fn default() -> FFIStatusCode { FFIStatusCode::Ok }
|
fn default() -> FFIStatusCode {
|
||||||
|
FFIStatusCode::Ok
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(ProtoBuf, Default)]
|
#[derive(ProtoBuf, Default)]
|
||||||
|
@ -13,7 +13,9 @@ pub struct DartStreamSender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DartStreamSender {
|
impl DartStreamSender {
|
||||||
fn new() -> Self { Self { isolate: None } }
|
fn new() -> Self {
|
||||||
|
Self { isolate: None }
|
||||||
|
}
|
||||||
|
|
||||||
fn inner_set_port(&mut self, port: i64) {
|
fn inner_set_port(&mut self, port: i64) {
|
||||||
log::info!("Setup rust to flutter stream with port {}", port);
|
log::info!("Setup rust to flutter stream with port {}", port);
|
||||||
@ -27,7 +29,7 @@ impl DartStreamSender {
|
|||||||
let bytes: Bytes = observable_subject.try_into().unwrap();
|
let bytes: Bytes = observable_subject.try_into().unwrap();
|
||||||
isolate.post(bytes.to_vec());
|
isolate.post(bytes.to_vec());
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
None => Err("Isolate is not set".to_owned()),
|
None => Err("Isolate is not set".to_owned()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -38,7 +40,7 @@ impl DartStreamSender {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
let msg = format!("Get rust to flutter stream lock fail. {:?}", e);
|
let msg = format!("Get rust to flutter stream lock fail. {:?}", e);
|
||||||
log::error!("{:?}", msg);
|
log::error!("{:?}", msg);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ impl DartNotifyBuilder {
|
|||||||
Ok(bytes) => self.payload = Some(bytes),
|
Ok(bytes) => self.payload = Some(bytes),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Set observable payload failed: {:?}", e);
|
log::error!("Set observable payload failed: {:?}", e);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
@ -48,7 +48,7 @@ impl DartNotifyBuilder {
|
|||||||
Ok(bytes) => self.error = Some(bytes),
|
Ok(bytes) => self.error = Some(bytes),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Set observable error failed: {:?}", e);
|
log::error!("Set observable error failed: {:?}", e);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ impl DartNotifyBuilder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match DartStreamSender::post(subject) {
|
match DartStreamSender::post(subject) {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(error) => log::error!("Send observable subject failed: {}", error),
|
Err(error) => log::error!("Send observable subject failed: {}", error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,10 +57,10 @@ impl CoreContext {
|
|||||||
|
|
||||||
pub fn network_state_changed(&self, new_type: NetworkType) {
|
pub fn network_state_changed(&self, new_type: NetworkType) {
|
||||||
match new_type {
|
match new_type {
|
||||||
NetworkType::UnknownNetworkType => {},
|
NetworkType::UnknownNetworkType => {}
|
||||||
NetworkType::Wifi => {},
|
NetworkType::Wifi => {}
|
||||||
NetworkType::Cell => {},
|
NetworkType::Cell => {}
|
||||||
NetworkType::Ethernet => {},
|
NetworkType::Ethernet => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,77 +5,77 @@ use strum_macros::Display;
|
|||||||
#[event_err = "FlowyError"]
|
#[event_err = "FlowyError"]
|
||||||
pub enum WorkspaceEvent {
|
pub enum WorkspaceEvent {
|
||||||
#[event(input = "CreateWorkspaceRequest", output = "Workspace")]
|
#[event(input = "CreateWorkspaceRequest", output = "Workspace")]
|
||||||
CreateWorkspace = 0,
|
CreateWorkspace = 0,
|
||||||
|
|
||||||
#[event(output = "CurrentWorkspaceSetting")]
|
#[event(output = "CurrentWorkspaceSetting")]
|
||||||
ReadCurWorkspace = 1,
|
ReadCurWorkspace = 1,
|
||||||
|
|
||||||
#[event(input = "QueryWorkspaceRequest", output = "RepeatedWorkspace")]
|
#[event(input = "QueryWorkspaceRequest", output = "RepeatedWorkspace")]
|
||||||
ReadWorkspaces = 2,
|
ReadWorkspaces = 2,
|
||||||
|
|
||||||
#[event(input = "QueryWorkspaceRequest")]
|
#[event(input = "QueryWorkspaceRequest")]
|
||||||
DeleteWorkspace = 3,
|
DeleteWorkspace = 3,
|
||||||
|
|
||||||
#[event(input = "QueryWorkspaceRequest", output = "Workspace")]
|
#[event(input = "QueryWorkspaceRequest", output = "Workspace")]
|
||||||
OpenWorkspace = 4,
|
OpenWorkspace = 4,
|
||||||
|
|
||||||
#[event(input = "QueryWorkspaceRequest", output = "RepeatedApp")]
|
#[event(input = "QueryWorkspaceRequest", output = "RepeatedApp")]
|
||||||
ReadWorkspaceApps = 5,
|
ReadWorkspaceApps = 5,
|
||||||
|
|
||||||
#[event(input = "CreateAppRequest", output = "App")]
|
#[event(input = "CreateAppRequest", output = "App")]
|
||||||
CreateApp = 101,
|
CreateApp = 101,
|
||||||
|
|
||||||
#[event(input = "QueryAppRequest")]
|
#[event(input = "QueryAppRequest")]
|
||||||
DeleteApp = 102,
|
DeleteApp = 102,
|
||||||
|
|
||||||
#[event(input = "QueryAppRequest", output = "App")]
|
#[event(input = "QueryAppRequest", output = "App")]
|
||||||
ReadApp = 103,
|
ReadApp = 103,
|
||||||
|
|
||||||
#[event(input = "UpdateAppRequest")]
|
#[event(input = "UpdateAppRequest")]
|
||||||
UpdateApp = 104,
|
UpdateApp = 104,
|
||||||
|
|
||||||
#[event(input = "CreateViewRequest", output = "View")]
|
#[event(input = "CreateViewRequest", output = "View")]
|
||||||
CreateView = 201,
|
CreateView = 201,
|
||||||
|
|
||||||
#[event(input = "QueryViewRequest", output = "View")]
|
#[event(input = "QueryViewRequest", output = "View")]
|
||||||
ReadView = 202,
|
ReadView = 202,
|
||||||
|
|
||||||
#[event(input = "UpdateViewRequest", output = "View")]
|
#[event(input = "UpdateViewRequest", output = "View")]
|
||||||
UpdateView = 203,
|
UpdateView = 203,
|
||||||
|
|
||||||
#[event(input = "QueryViewRequest")]
|
#[event(input = "QueryViewRequest")]
|
||||||
DeleteView = 204,
|
DeleteView = 204,
|
||||||
|
|
||||||
#[event(input = "QueryViewRequest")]
|
#[event(input = "QueryViewRequest")]
|
||||||
DuplicateView = 205,
|
DuplicateView = 205,
|
||||||
|
|
||||||
#[event()]
|
#[event()]
|
||||||
CopyLink = 206,
|
CopyLink = 206,
|
||||||
|
|
||||||
#[event(input = "QueryViewRequest", output = "DocumentDelta")]
|
#[event(input = "QueryViewRequest", output = "DocumentDelta")]
|
||||||
OpenView = 207,
|
OpenView = 207,
|
||||||
|
|
||||||
#[event(input = "QueryViewRequest")]
|
#[event(input = "QueryViewRequest")]
|
||||||
CloseView = 208,
|
CloseView = 208,
|
||||||
|
|
||||||
#[event(output = "RepeatedTrash")]
|
#[event(output = "RepeatedTrash")]
|
||||||
ReadTrash = 300,
|
ReadTrash = 300,
|
||||||
|
|
||||||
#[event(input = "TrashId")]
|
#[event(input = "TrashId")]
|
||||||
PutbackTrash = 301,
|
PutbackTrash = 301,
|
||||||
|
|
||||||
#[event(input = "RepeatedTrashId")]
|
#[event(input = "RepeatedTrashId")]
|
||||||
DeleteTrash = 302,
|
DeleteTrash = 302,
|
||||||
|
|
||||||
#[event()]
|
#[event()]
|
||||||
RestoreAll = 303,
|
RestoreAll = 303,
|
||||||
|
|
||||||
#[event()]
|
#[event()]
|
||||||
DeleteAll = 304,
|
DeleteAll = 304,
|
||||||
|
|
||||||
#[event(input = "DocumentDelta", output = "DocumentDelta")]
|
#[event(input = "DocumentDelta", output = "DocumentDelta")]
|
||||||
ApplyDocDelta = 400,
|
ApplyDocDelta = 400,
|
||||||
|
|
||||||
#[event(input = "ExportRequest", output = "ExportData")]
|
#[event(input = "ExportRequest", output = "ExportData")]
|
||||||
ExportDocument = 500,
|
ExportDocument = 500,
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,7 @@ use crate::{
|
|||||||
errors::FlowyError,
|
errors::FlowyError,
|
||||||
notify::{send_dart_notification, WorkspaceNotification},
|
notify::{send_dart_notification, WorkspaceNotification},
|
||||||
services::{
|
services::{
|
||||||
get_current_workspace,
|
get_current_workspace, read_local_workspace_apps,
|
||||||
read_local_workspace_apps,
|
|
||||||
workspace::sql::{WorkspaceTable, WorkspaceTableSql},
|
workspace::sql::{WorkspaceTable, WorkspaceTableSql},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -86,14 +85,14 @@ fn read_workspaces_on_server(
|
|||||||
for app in apps {
|
for app in apps {
|
||||||
let views = app.belongings.clone().into_inner();
|
let views = app.belongings.clone().into_inner();
|
||||||
match app_ctrl.save_app(app, &*conn) {
|
match app_ctrl.save_app(app, &*conn) {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => log::error!("create app failed: {:?}", e),
|
Err(e) => log::error!("create app failed: {:?}", e),
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::debug!("Save {} views", views.len());
|
tracing::debug!("Save {} views", views.len());
|
||||||
for view in views {
|
for view in views {
|
||||||
match view_ctrl.save_view(view, &*conn) {
|
match view_ctrl.save_view(view, &*conn) {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => log::error!("create view failed: {:?}", e),
|
Err(e) => log::error!("create view failed: {:?}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,15 +15,21 @@ macro_rules! impl_def_and_def_mut {
|
|||||||
impl std::ops::Deref for $target {
|
impl std::ops::Deref for $target {
|
||||||
type Target = Vec<$item>;
|
type Target = Vec<$item>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target { &self.items }
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.items
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl std::ops::DerefMut for $target {
|
impl std::ops::DerefMut for $target {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.items }
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.items
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl $target {
|
impl $target {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn into_inner(&mut self) -> Vec<$item> { ::std::mem::replace(&mut self.items, vec![]) }
|
pub fn into_inner(&mut self) -> Vec<$item> {
|
||||||
|
::std::mem::replace(&mut self.items, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn push(&mut self, item: $item) {
|
pub fn push(&mut self, item: $item) {
|
||||||
@ -35,7 +41,9 @@ macro_rules! impl_def_and_def_mut {
|
|||||||
self.items.push(item);
|
self.items.push(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn first_or_crash(&self) -> &$item { self.items.first().unwrap() }
|
pub fn first_or_crash(&self) -> &$item {
|
||||||
|
self.items.first().unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -6,15 +6,8 @@ use crate::{
|
|||||||
event::WorkspaceEvent,
|
event::WorkspaceEvent,
|
||||||
event_handler::*,
|
event_handler::*,
|
||||||
services::{
|
services::{
|
||||||
app::event_handler::*,
|
app::event_handler::*, server::construct_workspace_server, trash::event_handler::*, view::event_handler::*,
|
||||||
server::construct_workspace_server,
|
workspace::event_handler::*, AppController, TrashController, ViewController, WorkspaceController,
|
||||||
trash::event_handler::*,
|
|
||||||
view::event_handler::*,
|
|
||||||
workspace::event_handler::*,
|
|
||||||
AppController,
|
|
||||||
TrashController,
|
|
||||||
ViewController,
|
|
||||||
WorkspaceController,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use backend_service::configuration::ClientServerConfiguration;
|
use backend_service::configuration::ClientServerConfiguration;
|
||||||
|
@ -6,27 +6,31 @@ const OBSERVABLE_CATEGORY: &str = "Workspace";
|
|||||||
// be use directly in flutter
|
// be use directly in flutter
|
||||||
#[derive(ProtoBuf_Enum, Debug)]
|
#[derive(ProtoBuf_Enum, Debug)]
|
||||||
pub(crate) enum WorkspaceNotification {
|
pub(crate) enum WorkspaceNotification {
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
UserCreateWorkspace = 10,
|
UserCreateWorkspace = 10,
|
||||||
UserDeleteWorkspace = 11,
|
UserDeleteWorkspace = 11,
|
||||||
WorkspaceUpdated = 12,
|
WorkspaceUpdated = 12,
|
||||||
WorkspaceListUpdated = 13,
|
WorkspaceListUpdated = 13,
|
||||||
WorkspaceAppsChanged = 14,
|
WorkspaceAppsChanged = 14,
|
||||||
AppUpdated = 21,
|
AppUpdated = 21,
|
||||||
AppViewsChanged = 24,
|
AppViewsChanged = 24,
|
||||||
ViewUpdated = 31,
|
ViewUpdated = 31,
|
||||||
ViewDeleted = 32,
|
ViewDeleted = 32,
|
||||||
ViewRestored = 33,
|
ViewRestored = 33,
|
||||||
UserUnauthorized = 100,
|
UserUnauthorized = 100,
|
||||||
TrashUpdated = 1000,
|
TrashUpdated = 1000,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::default::Default for WorkspaceNotification {
|
impl std::default::Default for WorkspaceNotification {
|
||||||
fn default() -> Self { WorkspaceNotification::Unknown }
|
fn default() -> Self {
|
||||||
|
WorkspaceNotification::Unknown
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<WorkspaceNotification> for i32 {
|
impl std::convert::From<WorkspaceNotification> for i32 {
|
||||||
fn from(notification: WorkspaceNotification) -> Self { notification as i32 }
|
fn from(notification: WorkspaceNotification) -> Self {
|
||||||
|
notification as i32
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug")]
|
#[tracing::instrument(level = "debug")]
|
||||||
|
@ -9,8 +9,7 @@ use crate::{
|
|||||||
services::{
|
services::{
|
||||||
app::sql::{AppTable, AppTableChangeset, AppTableSql},
|
app::sql::{AppTable, AppTableChangeset, AppTableSql},
|
||||||
server::Server,
|
server::Server,
|
||||||
TrashController,
|
TrashController, TrashEvent,
|
||||||
TrashEvent,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use flowy_database::SqliteConnection;
|
use flowy_database::SqliteConnection;
|
||||||
@ -125,11 +124,11 @@ impl AppController {
|
|||||||
let server = self.server.clone();
|
let server = self.server.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
match server.update_app(&token, params).await {
|
match server.update_app(&token, params).await {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// TODO: retry?
|
// TODO: retry?
|
||||||
log::error!("Update app failed: {:?}", e);
|
log::error!("Update app failed: {:?}", e);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -152,13 +151,13 @@ impl AppController {
|
|||||||
send_dart_notification(&app.id, WorkspaceNotification::AppUpdated)
|
send_dart_notification(&app.id, WorkspaceNotification::AppUpdated)
|
||||||
.payload(app)
|
.payload(app)
|
||||||
.send();
|
.send();
|
||||||
},
|
}
|
||||||
Err(e) => log::error!("Save app failed: {:?}", e),
|
Err(e) => log::error!("Save app failed: {:?}", e),
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(e) => log::error!("Require db connection failed: {:?}", e),
|
Err(e) => log::error!("Require db connection failed: {:?}", e),
|
||||||
},
|
},
|
||||||
Ok(None) => {},
|
Ok(None) => {}
|
||||||
Err(e) => log::error!("Read app failed: {:?}", e),
|
Err(e) => log::error!("Read app failed: {:?}", e),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -202,7 +201,7 @@ async fn handle_trash_event(database: Arc<dyn WorkspaceDatabase>, trash_can: Arc
|
|||||||
Ok::<(), FlowyError>(())
|
Ok::<(), FlowyError>(())
|
||||||
};
|
};
|
||||||
let _ = ret.send(result()).await;
|
let _ = ret.send(result()).await;
|
||||||
},
|
}
|
||||||
TrashEvent::Delete(identifiers, ret) => {
|
TrashEvent::Delete(identifiers, ret) => {
|
||||||
let result = || {
|
let result = || {
|
||||||
let conn = &*db_result?;
|
let conn = &*db_result?;
|
||||||
@ -222,7 +221,7 @@ async fn handle_trash_event(database: Arc<dyn WorkspaceDatabase>, trash_can: Arc
|
|||||||
Ok::<(), FlowyError>(())
|
Ok::<(), FlowyError>(())
|
||||||
};
|
};
|
||||||
let _ = ret.send(result()).await;
|
let _ = ret.send(result()).await;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ impl AppTableSql {
|
|||||||
_ => {
|
_ => {
|
||||||
let changeset = AppTableChangeset::from_table(app_table);
|
let changeset = AppTableChangeset::from_table(app_table);
|
||||||
diesel_update_table!(app_table, changeset, conn)
|
diesel_update_table!(app_table, changeset, conn)
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -145,7 +145,9 @@ impl std::convert::From<ColorStyle> for ColorStyleCol {
|
|||||||
impl std::convert::TryInto<Vec<u8>> for &ColorStyleCol {
|
impl std::convert::TryInto<Vec<u8>> for &ColorStyleCol {
|
||||||
type Error = String;
|
type Error = String;
|
||||||
|
|
||||||
fn try_into(self) -> Result<Vec<u8>, Self::Error> { bincode::serialize(self).map_err(|e| format!("{:?}", e)) }
|
fn try_into(self) -> Result<Vec<u8>, Self::Error> {
|
||||||
|
bincode::serialize(self).map_err(|e| format!("{:?}", e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::TryFrom<&[u8]> for ColorStyleCol {
|
impl std::convert::TryFrom<&[u8]> for ColorStyleCol {
|
||||||
|
@ -17,7 +17,9 @@ pub struct WorkspaceHttpServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WorkspaceHttpServer {
|
impl WorkspaceHttpServer {
|
||||||
pub fn new(config: ClientServerConfiguration) -> WorkspaceHttpServer { Self { config } }
|
pub fn new(config: ClientServerConfiguration) -> WorkspaceHttpServer {
|
||||||
|
Self { config }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkspaceServerAPI for WorkspaceHttpServer {
|
impl WorkspaceServerAPI for WorkspaceHttpServer {
|
||||||
|
@ -29,7 +29,9 @@ impl TrashController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn init(&self) -> Result<(), FlowyError> { Ok(()) }
|
pub(crate) fn init(&self) -> Result<(), FlowyError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(self), fields(putback) err)]
|
#[tracing::instrument(level = "debug", skip(self), fields(putback) err)]
|
||||||
pub async fn putback(&self, trash_id: &str) -> FlowyResult<()> {
|
pub async fn putback(&self, trash_id: &str) -> FlowyResult<()> {
|
||||||
@ -112,9 +114,9 @@ impl TrashController {
|
|||||||
let _ = self.notify.send(TrashEvent::Delete(trash_identifiers.clone(), tx));
|
let _ = self.notify.send(TrashEvent::Delete(trash_identifiers.clone(), tx));
|
||||||
|
|
||||||
match rx.recv().await {
|
match rx.recv().await {
|
||||||
None => {},
|
None => {}
|
||||||
Some(result) => match result {
|
Some(result) => match result {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => log::error!("{}", e),
|
Err(e) => log::error!("{}", e),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -171,7 +173,9 @@ impl TrashController {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subscribe(&self) -> broadcast::Receiver<TrashEvent> { self.notify.subscribe() }
|
pub fn subscribe(&self) -> broadcast::Receiver<TrashEvent> {
|
||||||
|
self.notify.subscribe()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read_trash(&self, conn: &SqliteConnection) -> Result<RepeatedTrash, FlowyError> {
|
pub fn read_trash(&self, conn: &SqliteConnection) -> Result<RepeatedTrash, FlowyError> {
|
||||||
let repeated_trash = TrashTableSql::read_all(&*conn)?;
|
let repeated_trash = TrashTableSql::read_all(&*conn)?;
|
||||||
@ -198,7 +202,7 @@ impl TrashController {
|
|||||||
// TODO: retry?
|
// TODO: retry?
|
||||||
let _ = tokio::spawn(async move {
|
let _ = tokio::spawn(async move {
|
||||||
match server.create_trash(&token, trash_identifiers).await {
|
match server.create_trash(&token, trash_identifiers).await {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => log::error!("Create trash failed: {:?}", e),
|
Err(e) => log::error!("Create trash failed: {:?}", e),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -212,7 +216,7 @@ impl TrashController {
|
|||||||
let server = self.server.clone();
|
let server = self.server.clone();
|
||||||
let _ = tokio::spawn(async move {
|
let _ = tokio::spawn(async move {
|
||||||
match server.delete_trash(&token, trash_identifiers).await {
|
match server.delete_trash(&token, trash_identifiers).await {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => log::error!("Delete trash failed: {:?}", e),
|
Err(e) => log::error!("Delete trash failed: {:?}", e),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -239,13 +243,13 @@ impl TrashController {
|
|||||||
match result {
|
match result {
|
||||||
Ok(repeated_trash) => {
|
Ok(repeated_trash) => {
|
||||||
notify_trash_changed(repeated_trash);
|
notify_trash_changed(repeated_trash);
|
||||||
},
|
}
|
||||||
Err(e) => log::error!("Save trash failed: {:?}", e),
|
Err(e) => log::error!("Save trash failed: {:?}", e),
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(e) => log::error!("Require db connection failed: {:?}", e),
|
Err(e) => log::error!("Require db connection failed: {:?}", e),
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(e) => log::error!("Read trash failed: {:?}", e),
|
Err(e) => log::error!("Read trash failed: {:?}", e),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -295,7 +299,7 @@ impl TrashEvent {
|
|||||||
} else {
|
} else {
|
||||||
Some(TrashEvent::Putback(identifiers, sender))
|
Some(TrashEvent::Putback(identifiers, sender))
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
TrashEvent::Delete(mut identifiers, sender) => {
|
TrashEvent::Delete(mut identifiers, sender) => {
|
||||||
identifiers.items.retain(|item| item.ty == s);
|
identifiers.items.retain(|item| item.ty == s);
|
||||||
if identifiers.items.is_empty() {
|
if identifiers.items.is_empty() {
|
||||||
@ -303,7 +307,7 @@ impl TrashEvent {
|
|||||||
} else {
|
} else {
|
||||||
Some(TrashEvent::Delete(identifiers, sender))
|
Some(TrashEvent::Delete(identifiers, sender))
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
TrashEvent::NewTrash(mut identifiers, sender) => {
|
TrashEvent::NewTrash(mut identifiers, sender) => {
|
||||||
identifiers.items.retain(|item| item.ty == s);
|
identifiers.items.retain(|item| item.ty == s);
|
||||||
if identifiers.items.is_empty() {
|
if identifiers.items.is_empty() {
|
||||||
@ -311,7 +315,7 @@ impl TrashEvent {
|
|||||||
} else {
|
} else {
|
||||||
Some(TrashEvent::NewTrash(identifiers, sender))
|
Some(TrashEvent::NewTrash(identifiers, sender))
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ impl TrashTableSql {
|
|||||||
_ => {
|
_ => {
|
||||||
let changeset = TrashTableChangeset::from(trash_table);
|
let changeset = TrashTableChangeset::from(trash_table);
|
||||||
diesel_update_table!(trash_table, changeset, conn)
|
diesel_update_table!(trash_table, changeset, conn)
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,8 +109,8 @@ impl std::convert::From<TrashTable> for TrashTableChangeset {
|
|||||||
#[sql_type = "Integer"]
|
#[sql_type = "Integer"]
|
||||||
pub(crate) enum SqlTrashType {
|
pub(crate) enum SqlTrashType {
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
View = 1,
|
View = 1,
|
||||||
App = 2,
|
App = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<i32> for SqlTrashType {
|
impl std::convert::From<i32> for SqlTrashType {
|
||||||
|
@ -18,8 +18,7 @@ use crate::{
|
|||||||
services::{
|
services::{
|
||||||
server::Server,
|
server::Server,
|
||||||
view::sql::{ViewTable, ViewTableChangeset, ViewTableSql},
|
view::sql::{ViewTable, ViewTableChangeset, ViewTableSql},
|
||||||
TrashController,
|
TrashController, TrashEvent,
|
||||||
TrashEvent,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use flowy_core_data_model::entities::share::{ExportData, ExportParams};
|
use flowy_core_data_model::entities::share::{ExportData, ExportParams};
|
||||||
@ -84,7 +83,7 @@ impl ViewController {
|
|||||||
conn.immediate_transaction::<_, FlowyError, _>(|| {
|
conn.immediate_transaction::<_, FlowyError, _>(|| {
|
||||||
let belong_to_id = view.belong_to_id.clone();
|
let belong_to_id = view.belong_to_id.clone();
|
||||||
let _ = self.save_view(view, conn)?;
|
let _ = self.save_view(view, conn)?;
|
||||||
let _ = notify_views_changed(&belong_to_id, trash_can, &conn)?;
|
let _ = notify_views_changed(&belong_to_id, trash_can, conn)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
@ -227,11 +226,13 @@ impl ViewController {
|
|||||||
let conn = self.database.db_connection()?;
|
let conn = self.database.db_connection()?;
|
||||||
let view_table = ViewTableSql::read_view(&view_id, &*conn)?;
|
let view_table = ViewTableSql::read_view(&view_id, &*conn)?;
|
||||||
Ok(Some(view_table.into()))
|
Ok(Some(view_table.into()))
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_latest_view(&self, view: &View) { KV::set_str(LATEST_VIEW_ID, view.id.clone()); }
|
pub(crate) fn set_latest_view(&self, view: &View) {
|
||||||
|
KV::set_str(LATEST_VIEW_ID, view.id.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ViewController {
|
impl ViewController {
|
||||||
@ -248,11 +249,11 @@ impl ViewController {
|
|||||||
let server = self.server.clone();
|
let server = self.server.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
match server.update_view(&token, params).await {
|
match server.update_view(&token, params).await {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// TODO: retry?
|
// TODO: retry?
|
||||||
log::error!("Update view failed: {:?}", e);
|
log::error!("Update view failed: {:?}", e);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -275,13 +276,13 @@ impl ViewController {
|
|||||||
send_dart_notification(&view.id, WorkspaceNotification::ViewUpdated)
|
send_dart_notification(&view.id, WorkspaceNotification::ViewUpdated)
|
||||||
.payload(view.clone())
|
.payload(view.clone())
|
||||||
.send();
|
.send();
|
||||||
},
|
}
|
||||||
Err(e) => log::error!("Save view failed: {:?}", e),
|
Err(e) => log::error!("Save view failed: {:?}", e),
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(e) => log::error!("Require db connection failed: {:?}", e),
|
Err(e) => log::error!("Require db connection failed: {:?}", e),
|
||||||
},
|
},
|
||||||
Ok(None) => {},
|
Ok(None) => {}
|
||||||
Err(e) => log::error!("Read view failed: {:?}", e),
|
Err(e) => log::error!("Read view failed: {:?}", e),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -331,7 +332,7 @@ async fn handle_trash_event(
|
|||||||
Ok::<(), FlowyError>(())
|
Ok::<(), FlowyError>(())
|
||||||
};
|
};
|
||||||
let _ = ret.send(result()).await;
|
let _ = ret.send(result()).await;
|
||||||
},
|
}
|
||||||
TrashEvent::Putback(identifiers, ret) => {
|
TrashEvent::Putback(identifiers, ret) => {
|
||||||
let result = || {
|
let result = || {
|
||||||
let conn = &*db_result?;
|
let conn = &*db_result?;
|
||||||
@ -343,7 +344,7 @@ async fn handle_trash_event(
|
|||||||
Ok::<(), FlowyError>(())
|
Ok::<(), FlowyError>(())
|
||||||
};
|
};
|
||||||
let _ = ret.send(result()).await;
|
let _ = ret.send(result()).await;
|
||||||
},
|
}
|
||||||
TrashEvent::Delete(identifiers, ret) => {
|
TrashEvent::Delete(identifiers, ret) => {
|
||||||
let result = || {
|
let result = || {
|
||||||
let conn = &*db_result?;
|
let conn = &*db_result?;
|
||||||
@ -365,7 +366,7 @@ async fn handle_trash_event(
|
|||||||
Ok::<(), FlowyError>(())
|
Ok::<(), FlowyError>(())
|
||||||
};
|
};
|
||||||
let _ = ret.send(result()).await;
|
let _ = ret.send(result()).await;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,7 +395,7 @@ fn notify_views_changed(
|
|||||||
) -> FlowyResult<()> {
|
) -> FlowyResult<()> {
|
||||||
let repeated_view = read_belonging_views_on_local(belong_to_id, trash_controller.clone(), conn)?;
|
let repeated_view = read_belonging_views_on_local(belong_to_id, trash_controller.clone(), conn)?;
|
||||||
tracing::Span::current().record("view_count", &format!("{}", repeated_view.len()).as_str());
|
tracing::Span::current().record("view_count", &format!("{}", repeated_view.len()).as_str());
|
||||||
send_dart_notification(&belong_to_id, WorkspaceNotification::AppViewsChanged)
|
send_dart_notification(belong_to_id, WorkspaceNotification::AppViewsChanged)
|
||||||
.payload(repeated_view)
|
.payload(repeated_view)
|
||||||
.send();
|
.send();
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -2,14 +2,8 @@ use crate::{
|
|||||||
entities::{
|
entities::{
|
||||||
trash::Trash,
|
trash::Trash,
|
||||||
view::{
|
view::{
|
||||||
CreateViewParams,
|
CreateViewParams, CreateViewRequest, QueryViewRequest, RepeatedViewId, UpdateViewParams, UpdateViewRequest,
|
||||||
CreateViewRequest,
|
View, ViewId,
|
||||||
QueryViewRequest,
|
|
||||||
RepeatedViewId,
|
|
||||||
UpdateViewParams,
|
|
||||||
UpdateViewRequest,
|
|
||||||
View,
|
|
||||||
ViewId,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
errors::FlowyError,
|
errors::FlowyError,
|
||||||
|
@ -23,7 +23,7 @@ impl ViewTableSql {
|
|||||||
_ => {
|
_ => {
|
||||||
let changeset = ViewTableChangeset::from_table(view_table);
|
let changeset = ViewTableChangeset::from_table(view_table);
|
||||||
diesel_update_table!(view_table, changeset, conn)
|
diesel_update_table!(view_table, changeset, conn)
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -219,7 +219,9 @@ pub enum ViewTableType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl std::default::Default for ViewTableType {
|
impl std::default::Default for ViewTableType {
|
||||||
fn default() -> Self { ViewTableType::Docs }
|
fn default() -> Self {
|
||||||
|
ViewTableType::Docs
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<i32> for ViewTableType {
|
impl std::convert::From<i32> for ViewTableType {
|
||||||
@ -229,13 +231,15 @@ impl std::convert::From<i32> for ViewTableType {
|
|||||||
o => {
|
o => {
|
||||||
log::error!("Unsupported view type {}, fallback to ViewType::Docs", o);
|
log::error!("Unsupported view type {}, fallback to ViewType::Docs", o);
|
||||||
ViewTableType::Docs
|
ViewTableType::Docs
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ViewTableType {
|
impl ViewTableType {
|
||||||
pub fn value(&self) -> i32 { *self as i32 }
|
pub fn value(&self) -> i32 {
|
||||||
|
*self as i32
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_sql_integer_expression!(ViewTableType);
|
impl_sql_integer_expression!(ViewTableType);
|
||||||
|
@ -35,7 +35,9 @@ impl WorkspaceController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn init(&self) -> Result<(), FlowyError> { Ok(()) }
|
pub(crate) fn init(&self) -> Result<(), FlowyError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn create_workspace_from_params(
|
pub(crate) async fn create_workspace_from_params(
|
||||||
&self,
|
&self,
|
||||||
@ -124,7 +126,7 @@ impl WorkspaceController {
|
|||||||
set_current_workspace(&workspace.id);
|
set_current_workspace(&workspace.id);
|
||||||
Ok(workspace)
|
Ok(workspace)
|
||||||
} else {
|
} else {
|
||||||
return Err(FlowyError::workspace_id().context("Opened workspace id should not be empty"));
|
Err(FlowyError::workspace_id().context("Opened workspace id should not be empty"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,11 +193,11 @@ impl WorkspaceController {
|
|||||||
let (token, server) = (self.user.token()?, self.server.clone());
|
let (token, server) = (self.user.token()?, self.server.clone());
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
match server.update_workspace(&token, params).await {
|
match server.update_workspace(&token, params).await {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// TODO: retry?
|
// TODO: retry?
|
||||||
log::error!("Update workspace failed: {:?}", e);
|
log::error!("Update workspace failed: {:?}", e);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -209,11 +211,11 @@ impl WorkspaceController {
|
|||||||
let (token, server) = (self.user.token()?, self.server.clone());
|
let (token, server) = (self.user.token()?, self.server.clone());
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
match server.delete_workspace(&token, params).await {
|
match server.delete_workspace(&token, params).await {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// TODO: retry?
|
// TODO: retry?
|
||||||
log::error!("Delete workspace failed: {:?}", e);
|
log::error!("Delete workspace failed: {:?}", e);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -222,14 +224,16 @@ impl WorkspaceController {
|
|||||||
|
|
||||||
const CURRENT_WORKSPACE_ID: &str = "current_workspace_id";
|
const CURRENT_WORKSPACE_ID: &str = "current_workspace_id";
|
||||||
|
|
||||||
fn set_current_workspace(workspace_id: &str) { KV::set_str(CURRENT_WORKSPACE_ID, workspace_id.to_owned()); }
|
fn set_current_workspace(workspace_id: &str) {
|
||||||
|
KV::set_str(CURRENT_WORKSPACE_ID, workspace_id.to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_current_workspace() -> Result<String, FlowyError> {
|
pub fn get_current_workspace() -> Result<String, FlowyError> {
|
||||||
match KV::get_str(CURRENT_WORKSPACE_ID) {
|
match KV::get_str(CURRENT_WORKSPACE_ID) {
|
||||||
None => {
|
None => {
|
||||||
Err(FlowyError::record_not_found()
|
Err(FlowyError::record_not_found()
|
||||||
.context("Current workspace not found or should call open workspace first"))
|
.context("Current workspace not found or should call open workspace first"))
|
||||||
},
|
}
|
||||||
Some(workspace_id) => Ok(workspace_id),
|
Some(workspace_id) => Ok(workspace_id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ impl WorkspaceTableSql {
|
|||||||
_ => {
|
_ => {
|
||||||
let changeset = WorkspaceTableChangeset::from_table(table);
|
let changeset = WorkspaceTableChangeset::from_table(table);
|
||||||
diesel_update_table!(workspace_table, changeset, conn);
|
diesel_update_table!(workspace_table, changeset, conn);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ impl KV {
|
|||||||
match KV_HOLDER.write() {
|
match KV_HOLDER.write() {
|
||||||
Ok(mut guard) => {
|
Ok(mut guard) => {
|
||||||
guard.cache.remove(key);
|
guard.cache.remove(key);
|
||||||
},
|
}
|
||||||
Err(e) => log::error!("Require write lock failed: {:?}", e),
|
Err(e) => log::error!("Require write lock failed: {:?}", e),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ fn read_cache(key: &str) -> Option<KeyValue> {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Require read lock failed: {:?}", e);
|
log::error!("Require read lock failed: {:?}", e);
|
||||||
None
|
None
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ fn update_cache(value: KeyValue) {
|
|||||||
match KV_HOLDER.write() {
|
match KV_HOLDER.write() {
|
||||||
Ok(mut guard) => {
|
Ok(mut guard) => {
|
||||||
guard.cache.insert(value.key.clone(), value);
|
guard.cache.insert(value.key.clone(), value);
|
||||||
},
|
}
|
||||||
Err(e) => log::error!("Require write lock failed: {:?}", e),
|
Err(e) => log::error!("Require write lock failed: {:?}", e),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -132,10 +132,10 @@ macro_rules! impl_set_func {
|
|||||||
let mut item = KeyValue::new(key);
|
let mut item = KeyValue::new(key);
|
||||||
item.$set_method = Some(value);
|
item.$set_method = Some(value);
|
||||||
match KV::set(item) {
|
match KV::set(item) {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("{:?}", e)
|
log::error!("{:?}", e)
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,12 +168,12 @@ fn get_connection() -> Result<DBConnection, String> {
|
|||||||
.get_connection()
|
.get_connection()
|
||||||
.map_err(|e| format!("KVStore error: {:?}", e))?;
|
.map_err(|e| format!("KVStore error: {:?}", e))?;
|
||||||
Ok(conn)
|
Ok(conn)
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let msg = format!("KVStore get connection failed: {:?}", e);
|
let msg = format!("KVStore get connection failed: {:?}", e);
|
||||||
log::error!("{:?}", msg);
|
log::error!("{:?}", msg);
|
||||||
Err(msg)
|
Err(msg)
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ macro_rules! impl_sql_binary_expression {
|
|||||||
e
|
e
|
||||||
);
|
);
|
||||||
panic!();
|
panic!();
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,7 @@ use crate::{
|
|||||||
core::{
|
core::{
|
||||||
edit::ClientDocumentEditor,
|
edit::ClientDocumentEditor,
|
||||||
revision::{DocumentRevisionCache, DocumentRevisionManager, RevisionServer},
|
revision::{DocumentRevisionCache, DocumentRevisionManager, RevisionServer},
|
||||||
DocumentWSReceivers,
|
DocumentWSReceivers, DocumentWebSocket, WSStateReceiver,
|
||||||
DocumentWebSocket,
|
|
||||||
WSStateReceiver,
|
|
||||||
},
|
},
|
||||||
errors::FlowyError,
|
errors::FlowyError,
|
||||||
server::Server,
|
server::Server,
|
||||||
@ -101,8 +99,8 @@ impl DocumentController {
|
|||||||
match self.open_cache.get(doc_id) {
|
match self.open_cache.get(doc_id) {
|
||||||
None => {
|
None => {
|
||||||
let db_pool = self.user.db_pool()?;
|
let db_pool = self.user.db_pool()?;
|
||||||
self.make_editor(&doc_id, db_pool).await
|
self.make_editor(doc_id, db_pool).await
|
||||||
},
|
}
|
||||||
Some(editor) => Ok(editor),
|
Some(editor) => Ok(editor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,7 +121,7 @@ impl DocumentController {
|
|||||||
});
|
});
|
||||||
let doc_editor = ClientDocumentEditor::new(doc_id, user, rev_manager, self.ws_sender.clone(), server).await?;
|
let doc_editor = ClientDocumentEditor::new(doc_id, user, rev_manager, self.ws_sender.clone(), server).await?;
|
||||||
self.ws_receivers.add(doc_id, doc_editor.ws_handler());
|
self.ws_receivers.add(doc_id, doc_editor.ws_handler());
|
||||||
self.open_cache.insert(&doc_id, &doc_editor);
|
self.open_cache.insert(doc_id, &doc_editor);
|
||||||
Ok(doc_editor)
|
Ok(doc_editor)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +160,9 @@ pub struct OpenDocCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OpenDocCache {
|
impl OpenDocCache {
|
||||||
fn new() -> Self { Self { inner: DashMap::new() } }
|
fn new() -> Self {
|
||||||
|
Self { inner: DashMap::new() }
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn insert(&self, doc_id: &str, doc: &Arc<ClientDocumentEditor>) {
|
pub(crate) fn insert(&self, doc_id: &str, doc: &Arc<ClientDocumentEditor>) {
|
||||||
if self.inner.contains_key(doc_id) {
|
if self.inner.contains_key(doc_id) {
|
||||||
@ -171,10 +171,12 @@ impl OpenDocCache {
|
|||||||
self.inner.insert(doc_id.to_string(), doc.clone());
|
self.inner.insert(doc_id.to_string(), doc.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn contains(&self, doc_id: &str) -> bool { self.inner.get(doc_id).is_some() }
|
pub(crate) fn contains(&self, doc_id: &str) -> bool {
|
||||||
|
self.inner.get(doc_id).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn get(&self, doc_id: &str) -> Option<Arc<ClientDocumentEditor>> {
|
pub(crate) fn get(&self, doc_id: &str) -> Option<Arc<ClientDocumentEditor>> {
|
||||||
if !self.contains(&doc_id) {
|
if !self.contains(doc_id) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let opened_doc = self.inner.get(doc_id).unwrap();
|
let opened_doc = self.inner.get(doc_id).unwrap();
|
||||||
|
@ -151,9 +151,13 @@ impl ClientDocumentEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(self))]
|
#[tracing::instrument(level = "debug", skip(self))]
|
||||||
pub fn stop(&self) { self.ws_manager.stop(); }
|
pub fn stop(&self) {
|
||||||
|
self.ws_manager.stop();
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn ws_handler(&self) -> Arc<dyn DocumentWSReceiver> { self.ws_manager.receiver() }
|
pub(crate) fn ws_handler(&self) -> Arc<dyn DocumentWSReceiver> {
|
||||||
|
self.ws_manager.receiver()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_edit_queue(
|
fn spawn_edit_queue(
|
||||||
@ -185,5 +189,7 @@ impl ClientDocumentEditor {
|
|||||||
Ok(delta)
|
Ok(delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rev_manager(&self) -> Arc<DocumentRevisionManager> { self.rev_manager.clone() }
|
pub fn rev_manager(&self) -> Arc<DocumentRevisionManager> {
|
||||||
|
self.rev_manager.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ impl EditorCommandQueue {
|
|||||||
stream
|
stream
|
||||||
.for_each(|command| async {
|
.for_each(|command| async {
|
||||||
match self.handle_command(command).await {
|
match self.handle_command(command).await {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => tracing::debug!("[EditCommandQueue]: {}", e),
|
Err(e) => tracing::debug!("[EditCommandQueue]: {}", e),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -68,7 +68,7 @@ impl EditorCommandQueue {
|
|||||||
drop(document);
|
drop(document);
|
||||||
let _ = self.save_local_delta(delta, md5).await?;
|
let _ = self.save_local_delta(delta, md5).await?;
|
||||||
let _ = ret.send(Ok(()));
|
let _ = ret.send(Ok(()));
|
||||||
},
|
}
|
||||||
EditorCommand::ComposeRemoteDelta {
|
EditorCommand::ComposeRemoteDelta {
|
||||||
revisions,
|
revisions,
|
||||||
client_delta,
|
client_delta,
|
||||||
@ -96,7 +96,7 @@ impl EditorCommandQueue {
|
|||||||
);
|
);
|
||||||
let _ = self.rev_manager.add_remote_revision(&client_revision).await?;
|
let _ = self.rev_manager.add_remote_revision(&client_revision).await?;
|
||||||
let _ = ret.send(Ok(server_revision));
|
let _ = ret.send(Ok(server_revision));
|
||||||
},
|
}
|
||||||
EditorCommand::OverrideDelta { revisions, delta, ret } => {
|
EditorCommand::OverrideDelta { revisions, delta, ret } => {
|
||||||
let mut document = self.document.write().await;
|
let mut document = self.document.write().await;
|
||||||
let _ = document.set_delta(delta);
|
let _ = document.set_delta(delta);
|
||||||
@ -107,7 +107,7 @@ impl EditorCommandQueue {
|
|||||||
assert_eq!(repeated_revision.last().unwrap().md5, md5);
|
assert_eq!(repeated_revision.last().unwrap().md5, md5);
|
||||||
let _ = self.rev_manager.reset_document(repeated_revision).await?;
|
let _ = self.rev_manager.reset_document(repeated_revision).await?;
|
||||||
let _ = ret.send(Ok(()));
|
let _ = ret.send(Ok(()));
|
||||||
},
|
}
|
||||||
EditorCommand::TransformRevision { revisions, ret } => {
|
EditorCommand::TransformRevision { revisions, ret } => {
|
||||||
let f = || async {
|
let f = || async {
|
||||||
let new_delta = make_delta_from_revisions(revisions)?;
|
let new_delta = make_delta_from_revisions(revisions)?;
|
||||||
@ -130,21 +130,21 @@ impl EditorCommandQueue {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
let _ = ret.send(f().await);
|
let _ = ret.send(f().await);
|
||||||
},
|
}
|
||||||
EditorCommand::Insert { index, data, ret } => {
|
EditorCommand::Insert { index, data, ret } => {
|
||||||
let mut write_guard = self.document.write().await;
|
let mut write_guard = self.document.write().await;
|
||||||
let delta = write_guard.insert(index, data)?;
|
let delta = write_guard.insert(index, data)?;
|
||||||
let md5 = write_guard.md5();
|
let md5 = write_guard.md5();
|
||||||
let _ = self.save_local_delta(delta, md5).await?;
|
let _ = self.save_local_delta(delta, md5).await?;
|
||||||
let _ = ret.send(Ok(()));
|
let _ = ret.send(Ok(()));
|
||||||
},
|
}
|
||||||
EditorCommand::Delete { interval, ret } => {
|
EditorCommand::Delete { interval, ret } => {
|
||||||
let mut write_guard = self.document.write().await;
|
let mut write_guard = self.document.write().await;
|
||||||
let delta = write_guard.delete(interval)?;
|
let delta = write_guard.delete(interval)?;
|
||||||
let md5 = write_guard.md5();
|
let md5 = write_guard.md5();
|
||||||
let _ = self.save_local_delta(delta, md5).await?;
|
let _ = self.save_local_delta(delta, md5).await?;
|
||||||
let _ = ret.send(Ok(()));
|
let _ = ret.send(Ok(()));
|
||||||
},
|
}
|
||||||
EditorCommand::Format {
|
EditorCommand::Format {
|
||||||
interval,
|
interval,
|
||||||
attribute,
|
attribute,
|
||||||
@ -155,42 +155,42 @@ impl EditorCommandQueue {
|
|||||||
let md5 = write_guard.md5();
|
let md5 = write_guard.md5();
|
||||||
let _ = self.save_local_delta(delta, md5).await?;
|
let _ = self.save_local_delta(delta, md5).await?;
|
||||||
let _ = ret.send(Ok(()));
|
let _ = ret.send(Ok(()));
|
||||||
},
|
}
|
||||||
EditorCommand::Replace { interval, data, ret } => {
|
EditorCommand::Replace { interval, data, ret } => {
|
||||||
let mut write_guard = self.document.write().await;
|
let mut write_guard = self.document.write().await;
|
||||||
let delta = write_guard.replace(interval, data)?;
|
let delta = write_guard.replace(interval, data)?;
|
||||||
let md5 = write_guard.md5();
|
let md5 = write_guard.md5();
|
||||||
let _ = self.save_local_delta(delta, md5).await?;
|
let _ = self.save_local_delta(delta, md5).await?;
|
||||||
let _ = ret.send(Ok(()));
|
let _ = ret.send(Ok(()));
|
||||||
},
|
}
|
||||||
EditorCommand::CanUndo { ret } => {
|
EditorCommand::CanUndo { ret } => {
|
||||||
let _ = ret.send(self.document.read().await.can_undo());
|
let _ = ret.send(self.document.read().await.can_undo());
|
||||||
},
|
}
|
||||||
EditorCommand::CanRedo { ret } => {
|
EditorCommand::CanRedo { ret } => {
|
||||||
let _ = ret.send(self.document.read().await.can_redo());
|
let _ = ret.send(self.document.read().await.can_redo());
|
||||||
},
|
}
|
||||||
EditorCommand::Undo { ret } => {
|
EditorCommand::Undo { ret } => {
|
||||||
let mut write_guard = self.document.write().await;
|
let mut write_guard = self.document.write().await;
|
||||||
let UndoResult { delta } = write_guard.undo()?;
|
let UndoResult { delta } = write_guard.undo()?;
|
||||||
let md5 = write_guard.md5();
|
let md5 = write_guard.md5();
|
||||||
let _ = self.save_local_delta(delta, md5).await?;
|
let _ = self.save_local_delta(delta, md5).await?;
|
||||||
let _ = ret.send(Ok(()));
|
let _ = ret.send(Ok(()));
|
||||||
},
|
}
|
||||||
EditorCommand::Redo { ret } => {
|
EditorCommand::Redo { ret } => {
|
||||||
let mut write_guard = self.document.write().await;
|
let mut write_guard = self.document.write().await;
|
||||||
let UndoResult { delta } = write_guard.redo()?;
|
let UndoResult { delta } = write_guard.redo()?;
|
||||||
let md5 = write_guard.md5();
|
let md5 = write_guard.md5();
|
||||||
let _ = self.save_local_delta(delta, md5).await?;
|
let _ = self.save_local_delta(delta, md5).await?;
|
||||||
let _ = ret.send(Ok(()));
|
let _ = ret.send(Ok(()));
|
||||||
},
|
}
|
||||||
EditorCommand::ReadDoc { ret } => {
|
EditorCommand::ReadDoc { ret } => {
|
||||||
let data = self.document.read().await.to_json();
|
let data = self.document.read().await.to_json();
|
||||||
let _ = ret.send(Ok(data));
|
let _ = ret.send(Ok(data));
|
||||||
},
|
}
|
||||||
EditorCommand::ReadDocDelta { ret } => {
|
EditorCommand::ReadDocDelta { ret } => {
|
||||||
let delta = self.document.read().await.delta().clone();
|
let delta = self.document.read().await.delta().clone();
|
||||||
let _ = ret.send(Ok(delta));
|
let _ = ret.send(Ok(delta));
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -215,20 +215,20 @@ fn make_client_and_server_revision(
|
|||||||
md5: DocumentMD5,
|
md5: DocumentMD5,
|
||||||
) -> (Revision, Option<Revision>) {
|
) -> (Revision, Option<Revision>) {
|
||||||
let client_revision = Revision::new(
|
let client_revision = Revision::new(
|
||||||
&doc_id,
|
doc_id,
|
||||||
base_rev_id,
|
base_rev_id,
|
||||||
rev_id,
|
rev_id,
|
||||||
client_delta.to_bytes(),
|
client_delta.to_bytes(),
|
||||||
&user_id,
|
user_id,
|
||||||
md5.clone(),
|
md5.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
match server_delta {
|
match server_delta {
|
||||||
None => (client_revision, None),
|
None => (client_revision, None),
|
||||||
Some(server_delta) => {
|
Some(server_delta) => {
|
||||||
let server_revision = Revision::new(&doc_id, base_rev_id, rev_id, server_delta.to_bytes(), &user_id, md5);
|
let server_revision = Revision::new(doc_id, base_rev_id, rev_id, server_delta.to_bytes(), user_id, md5);
|
||||||
(client_revision, Some(server_revision))
|
(client_revision, Some(server_revision))
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +59,9 @@ impl DocumentRevisionCache {
|
|||||||
Ok(record)
|
Ok(record)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn ack(&self, rev_id: i64) { self.memory_cache.ack(&rev_id).await; }
|
pub async fn ack(&self, rev_id: i64) {
|
||||||
|
self.memory_cache.ack(&rev_id).await;
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get(&self, rev_id: i64) -> Option<RevisionRecord> {
|
pub async fn get(&self, rev_id: i64) -> Option<RevisionRecord> {
|
||||||
match self.memory_cache.get(&rev_id).await {
|
match self.memory_cache.get(&rev_id).await {
|
||||||
@ -69,11 +71,11 @@ impl DocumentRevisionCache {
|
|||||||
assert_eq!(records.len(), 1);
|
assert_eq!(records.len(), 1);
|
||||||
}
|
}
|
||||||
records.pop()
|
records.pop()
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("{}", e);
|
tracing::error!("{}", e);
|
||||||
None
|
None
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
Some(revision) => Some(revision),
|
Some(revision) => Some(revision),
|
||||||
}
|
}
|
||||||
@ -141,7 +143,7 @@ impl RevisionMemoryCacheDelegate for Arc<SQLitePersistence> {
|
|||||||
"checkpoint_result",
|
"checkpoint_result",
|
||||||
&format!("{} records were saved", records.len()).as_str(),
|
&format!("{} records were saved", records.len()).as_str(),
|
||||||
);
|
);
|
||||||
let _ = self.write_revision_records(records, &conn)?;
|
let _ = self.write_revision_records(records, conn)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -153,7 +155,7 @@ impl RevisionMemoryCacheDelegate for Arc<SQLitePersistence> {
|
|||||||
state: RevisionTableState::Ack,
|
state: RevisionTableState::Ack,
|
||||||
};
|
};
|
||||||
match self.update_revision_record(vec![changeset]) {
|
match self.update_revision_record(vec![changeset]) {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => tracing::error!("{}", e),
|
Err(e) => tracing::error!("{}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -167,5 +169,7 @@ pub struct RevisionRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RevisionRecord {
|
impl RevisionRecord {
|
||||||
pub fn ack(&mut self) { self.state = RevisionState::Ack; }
|
pub fn ack(&mut self) {
|
||||||
|
self.state = RevisionState::Ack;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,11 +196,13 @@ pub(crate) struct RevisionTable {
|
|||||||
#[sql_type = "Integer"]
|
#[sql_type = "Integer"]
|
||||||
pub enum RevisionTableState {
|
pub enum RevisionTableState {
|
||||||
Local = 0,
|
Local = 0,
|
||||||
Ack = 1,
|
Ack = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::default::Default for RevisionTableState {
|
impl std::default::Default for RevisionTableState {
|
||||||
fn default() -> Self { RevisionTableState::Local }
|
fn default() -> Self {
|
||||||
|
RevisionTableState::Local
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<i32> for RevisionTableState {
|
impl std::convert::From<i32> for RevisionTableState {
|
||||||
@ -211,13 +213,15 @@ impl std::convert::From<i32> for RevisionTableState {
|
|||||||
o => {
|
o => {
|
||||||
log::error!("Unsupported rev state {}, fallback to RevState::Local", o);
|
log::error!("Unsupported rev state {}, fallback to RevState::Local", o);
|
||||||
RevisionTableState::Local
|
RevisionTableState::Local
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RevisionTableState {
|
impl RevisionTableState {
|
||||||
pub fn value(&self) -> i32 { *self as i32 }
|
pub fn value(&self) -> i32 {
|
||||||
|
*self as i32
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl_sql_integer_expression!(RevisionTableState);
|
impl_sql_integer_expression!(RevisionTableState);
|
||||||
|
|
||||||
@ -246,7 +250,7 @@ pub(crate) fn mk_revision_record_from_table(user_id: &str, table: RevisionTable)
|
|||||||
table.base_rev_id,
|
table.base_rev_id,
|
||||||
table.rev_id,
|
table.rev_id,
|
||||||
Bytes::from(table.data),
|
Bytes::from(table.data),
|
||||||
&user_id,
|
user_id,
|
||||||
md5,
|
md5,
|
||||||
);
|
);
|
||||||
RevisionRecord {
|
RevisionRecord {
|
||||||
@ -260,12 +264,14 @@ pub(crate) fn mk_revision_record_from_table(user_id: &str, table: RevisionTable)
|
|||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
#[sql_type = "Integer"]
|
#[sql_type = "Integer"]
|
||||||
pub enum RevTableType {
|
pub enum RevTableType {
|
||||||
Local = 0,
|
Local = 0,
|
||||||
Remote = 1,
|
Remote = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::default::Default for RevTableType {
|
impl std::default::Default for RevTableType {
|
||||||
fn default() -> Self { RevTableType::Local }
|
fn default() -> Self {
|
||||||
|
RevTableType::Local
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<i32> for RevTableType {
|
impl std::convert::From<i32> for RevTableType {
|
||||||
@ -276,12 +282,14 @@ impl std::convert::From<i32> for RevTableType {
|
|||||||
o => {
|
o => {
|
||||||
log::error!("Unsupported rev type {}, fallback to RevTableType::Local", o);
|
log::error!("Unsupported rev type {}, fallback to RevTableType::Local", o);
|
||||||
RevTableType::Local
|
RevTableType::Local
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl RevTableType {
|
impl RevTableType {
|
||||||
pub fn value(&self) -> i32 { *self as i32 }
|
pub fn value(&self) -> i32 {
|
||||||
|
*self as i32
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl_sql_integer_expression!(RevTableType);
|
impl_sql_integer_expression!(RevTableType);
|
||||||
|
|
||||||
|
@ -97,9 +97,13 @@ impl DocumentRevisionManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rev_id(&self) -> i64 { self.rev_id_counter.value() }
|
pub fn rev_id(&self) -> i64 {
|
||||||
|
self.rev_id_counter.value()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_rev_id(&self, rev_id: i64) { self.rev_id_counter.set(rev_id); }
|
pub fn set_rev_id(&self, rev_id: i64) {
|
||||||
|
self.rev_id_counter.set(rev_id);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn next_rev_id_pair(&self) -> (i64, i64) {
|
pub fn next_rev_id_pair(&self) -> (i64, i64) {
|
||||||
let cur = self.rev_id_counter.value();
|
let cur = self.rev_id_counter.value();
|
||||||
@ -127,7 +131,9 @@ impl DocumentRevisionManager {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn latest_revision(&self) -> Revision { self.cache.latest_revision().await }
|
pub async fn latest_revision(&self) -> Revision {
|
||||||
|
self.cache.latest_revision().await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_revision(&self, rev_id: i64) -> Option<Revision> {
|
pub async fn get_revision(&self, rev_id: i64) -> Option<Revision> {
|
||||||
self.cache.get(rev_id).await.map(|record| record.revision)
|
self.cache.get(rev_id).await.map(|record| record.revision)
|
||||||
@ -150,7 +156,9 @@ impl std::default::Default for RevisionSyncSequence {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RevisionSyncSequence {
|
impl RevisionSyncSequence {
|
||||||
fn new() -> Self { RevisionSyncSequence::default() }
|
fn new() -> Self {
|
||||||
|
RevisionSyncSequence::default()
|
||||||
|
}
|
||||||
|
|
||||||
async fn add_revision(&self, record: RevisionRecord) -> Result<(), OTError> {
|
async fn add_revision(&self, record: RevisionRecord) -> Result<(), OTError> {
|
||||||
// The last revision's rev_id must be greater than the new one.
|
// The last revision's rev_id must be greater than the new one.
|
||||||
@ -189,7 +197,9 @@ impl RevisionSyncSequence {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn next_sync_rev_id(&self) -> Option<i64> { self.local_revs.read().await.front().copied() }
|
async fn next_sync_rev_id(&self) -> Option<i64> {
|
||||||
|
self.local_revs.read().await.front().copied()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RevisionLoader {
|
struct RevisionLoader {
|
||||||
@ -223,7 +233,7 @@ impl RevisionLoader {
|
|||||||
.filter(|record| future::ready(record.state == RevisionState::Local))
|
.filter(|record| future::ready(record.state == RevisionState::Local))
|
||||||
.for_each(|record| async move {
|
.for_each(|record| async move {
|
||||||
match self.cache.add(record.revision, record.state, false).await {
|
match self.cache.add(record.revision, record.state, false).await {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => tracing::error!("{}", e),
|
Err(e) => tracing::error!("{}", e),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -268,12 +278,18 @@ fn correct_delta(delta: &mut RichTextDelta) {
|
|||||||
#[cfg(feature = "flowy_unit_test")]
|
#[cfg(feature = "flowy_unit_test")]
|
||||||
impl RevisionSyncSequence {
|
impl RevisionSyncSequence {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn revs_map(&self) -> Arc<DashMap<i64, RevisionRecord>> { self.revs_map.clone() }
|
pub fn revs_map(&self) -> Arc<DashMap<i64, RevisionRecord>> {
|
||||||
|
self.revs_map.clone()
|
||||||
|
}
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn pending_revs(&self) -> Arc<RwLock<VecDeque<i64>>> { self.local_revs.clone() }
|
pub fn pending_revs(&self) -> Arc<RwLock<VecDeque<i64>>> {
|
||||||
|
self.local_revs.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "flowy_unit_test")]
|
#[cfg(feature = "flowy_unit_test")]
|
||||||
impl DocumentRevisionManager {
|
impl DocumentRevisionManager {
|
||||||
pub fn revision_cache(&self) -> Arc<DocumentRevisionCache> { self.cache.clone() }
|
pub fn revision_cache(&self) -> Arc<DocumentRevisionCache> {
|
||||||
|
self.cache.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,9 @@ impl DocumentRevisionMemoryCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn contains(&self, rev_id: &i64) -> bool { self.revs_map.contains_key(rev_id) }
|
pub(crate) fn contains(&self, rev_id: &i64) -> bool {
|
||||||
|
self.revs_map.contains_key(rev_id)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn add<'a>(&'a self, record: Cow<'a, RevisionRecord>) {
|
pub(crate) async fn add<'a>(&'a self, record: Cow<'a, RevisionRecord>) {
|
||||||
let record = match record {
|
let record = match record {
|
||||||
@ -55,7 +57,7 @@ impl DocumentRevisionMemoryCache {
|
|||||||
|
|
||||||
pub(crate) async fn ack(&self, rev_id: &i64) {
|
pub(crate) async fn ack(&self, rev_id: &i64) {
|
||||||
match self.revs_map.get_mut(rev_id) {
|
match self.revs_map.get_mut(rev_id) {
|
||||||
None => {},
|
None => {}
|
||||||
Some(mut record) => record.ack(),
|
Some(mut record) => record.ack(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +71,7 @@ impl DocumentRevisionMemoryCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get(&self, rev_id: &i64) -> Option<RevisionRecord> {
|
pub(crate) async fn get(&self, rev_id: &i64) -> Option<RevisionRecord> {
|
||||||
self.revs_map.get(&rev_id).map(|r| r.value().clone())
|
self.revs_map.get(rev_id).map(|r| r.value().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_with_range(&self, range: &RevisionRange) -> Result<Vec<RevisionRecord>, FlowyError> {
|
pub(crate) async fn get_with_range(&self, range: &RevisionRange) -> Result<Vec<RevisionRecord>, FlowyError> {
|
||||||
@ -123,10 +125,10 @@ impl DocumentRevisionMemoryCache {
|
|||||||
// https://stackoverflow.com/questions/28952411/what-is-the-idiomatic-way-to-pop-the-last-n-elements-in-a-mutable-vec
|
// https://stackoverflow.com/questions/28952411/what-is-the-idiomatic-way-to-pop-the-last-n-elements-in-a-mutable-vec
|
||||||
let mut save_records: Vec<RevisionRecord> = vec![];
|
let mut save_records: Vec<RevisionRecord> = vec![];
|
||||||
revs_write_guard.iter().for_each(|rev_id| match rev_map.get(rev_id) {
|
revs_write_guard.iter().for_each(|rev_id| match rev_map.get(rev_id) {
|
||||||
None => {},
|
None => {}
|
||||||
Some(value) => {
|
Some(value) => {
|
||||||
save_records.push(value.value().clone());
|
save_records.push(value.value().clone());
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if delegate.checkpoint_tick(save_records).is_ok() {
|
if delegate.checkpoint_tick(save_records).is_ok() {
|
||||||
|
@ -15,8 +15,7 @@ use lib_ws::WSConnectState;
|
|||||||
use std::{convert::TryFrom, sync::Arc};
|
use std::{convert::TryFrom, sync::Arc};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
sync::{
|
sync::{
|
||||||
broadcast,
|
broadcast, mpsc,
|
||||||
mpsc,
|
|
||||||
mpsc::{UnboundedReceiver, UnboundedSender},
|
mpsc::{UnboundedReceiver, UnboundedSender},
|
||||||
},
|
},
|
||||||
task::spawn_blocking,
|
task::spawn_blocking,
|
||||||
@ -77,7 +76,9 @@ impl HttpWebSocketManager {
|
|||||||
tokio::spawn(stream.run());
|
tokio::spawn(stream.run());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scribe_state(&self) -> broadcast::Receiver<WSConnectState> { self.state.subscribe() }
|
pub fn scribe_state(&self) -> broadcast::Receiver<WSConnectState> {
|
||||||
|
self.state.subscribe()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DocumentWebSocketManager for Arc<HttpWebSocketManager> {
|
impl DocumentWebSocketManager for Arc<HttpWebSocketManager> {
|
||||||
@ -87,27 +88,31 @@ impl DocumentWebSocketManager for Arc<HttpWebSocketManager> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn receiver(&self) -> Arc<dyn DocumentWSReceiver> { self.clone() }
|
fn receiver(&self) -> Arc<dyn DocumentWSReceiver> {
|
||||||
|
self.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DocumentWSReceiver for HttpWebSocketManager {
|
impl DocumentWSReceiver for HttpWebSocketManager {
|
||||||
fn receive_ws_data(&self, doc_data: DocumentServerWSData) {
|
fn receive_ws_data(&self, doc_data: DocumentServerWSData) {
|
||||||
match self.ws_msg_tx.send(doc_data) {
|
match self.ws_msg_tx.send(doc_data) {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => tracing::error!("❌ Propagate ws message failed. {}", e),
|
Err(e) => tracing::error!("❌ Propagate ws message failed. {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connect_state_changed(&self, state: &WSConnectState) {
|
fn connect_state_changed(&self, state: &WSConnectState) {
|
||||||
match self.state.send(state.clone()) {
|
match self.state.send(state.clone()) {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => tracing::error!("{}", e),
|
Err(e) => tracing::error!("{}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Drop for HttpWebSocketManager {
|
impl std::ops::Drop for HttpWebSocketManager {
|
||||||
fn drop(&mut self) { tracing::debug!("{} HttpWebSocketManager was drop", self.doc_id) }
|
fn drop(&mut self) {
|
||||||
|
tracing::debug!("{} HttpWebSocketManager was drop", self.doc_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait DocumentWSSteamConsumer: Send + Sync {
|
pub trait DocumentWSSteamConsumer: Send + Sync {
|
||||||
@ -168,7 +173,7 @@ impl DocumentWSStream {
|
|||||||
stream
|
stream
|
||||||
.for_each(|msg| async {
|
.for_each(|msg| async {
|
||||||
match self.handle_message(msg).await {
|
match self.handle_message(msg).await {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => log::error!("[DocumentStream:{}] error: {}", self.doc_id, e),
|
Err(e) => log::error!("[DocumentStream:{}] error: {}", self.doc_id, e),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -185,20 +190,20 @@ impl DocumentWSStream {
|
|||||||
match ty {
|
match ty {
|
||||||
DocumentServerWSDataType::ServerPushRev => {
|
DocumentServerWSDataType::ServerPushRev => {
|
||||||
let _ = self.consumer.receive_push_revision(bytes).await?;
|
let _ = self.consumer.receive_push_revision(bytes).await?;
|
||||||
},
|
}
|
||||||
DocumentServerWSDataType::ServerPullRev => {
|
DocumentServerWSDataType::ServerPullRev => {
|
||||||
let range = RevisionRange::try_from(bytes)?;
|
let range = RevisionRange::try_from(bytes)?;
|
||||||
let _ = self.consumer.pull_revisions_in_range(range).await?;
|
let _ = self.consumer.pull_revisions_in_range(range).await?;
|
||||||
},
|
}
|
||||||
DocumentServerWSDataType::ServerAck => {
|
DocumentServerWSDataType::ServerAck => {
|
||||||
let rev_id = RevId::try_from(bytes).unwrap().value;
|
let rev_id = RevId::try_from(bytes).unwrap().value;
|
||||||
let _ = self.consumer.receive_ack(rev_id.to_string(), ty).await;
|
let _ = self.consumer.receive_ack(rev_id.to_string(), ty).await;
|
||||||
},
|
}
|
||||||
DocumentServerWSDataType::UserConnect => {
|
DocumentServerWSDataType::UserConnect => {
|
||||||
let new_user = NewDocumentUser::try_from(bytes)?;
|
let new_user = NewDocumentUser::try_from(bytes)?;
|
||||||
let _ = self.consumer.receive_new_user_connect(new_user).await;
|
let _ = self.consumer.receive_new_user_connect(new_user).await;
|
||||||
// Notify the user that someone has connected to this document
|
// Notify the user that someone has connected to this document
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -258,7 +263,7 @@ impl DocumentWSSink {
|
|||||||
stream
|
stream
|
||||||
.for_each(|_| async {
|
.for_each(|_| async {
|
||||||
match self.send_next_revision().await {
|
match self.send_next_revision().await {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => log::error!("[DocumentSink]: Send failed, {:?}", e),
|
Err(e) => log::error!("[DocumentSink]: Send failed, {:?}", e),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -270,12 +275,12 @@ impl DocumentWSSink {
|
|||||||
None => {
|
None => {
|
||||||
tracing::trace!("Finish synchronizing revisions");
|
tracing::trace!("Finish synchronizing revisions");
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
tracing::trace!("[DocumentSink]: send: {}:{}-{:?}", data.doc_id, data.id(), data.ty);
|
tracing::trace!("[DocumentSink]: send: {}:{}-{:?}", data.doc_id, data.id(), data.ty);
|
||||||
self.ws_sender.send(data)
|
self.ws_sender.send(data)
|
||||||
// let _ = tokio::time::timeout(Duration::from_millis(2000),
|
// let _ = tokio::time::timeout(Duration::from_millis(2000),
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,9 @@ pub(crate) struct LocalWebSocketManager {}
|
|||||||
impl DocumentWebSocketManager for Arc<LocalWebSocketManager> {
|
impl DocumentWebSocketManager for Arc<LocalWebSocketManager> {
|
||||||
fn stop(&self) {}
|
fn stop(&self) {}
|
||||||
|
|
||||||
fn receiver(&self) -> Arc<dyn DocumentWSReceiver> { self.clone() }
|
fn receiver(&self) -> Arc<dyn DocumentWSReceiver> {
|
||||||
|
self.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DocumentWSReceiver for LocalWebSocketManager {
|
impl DocumentWSReceiver for LocalWebSocketManager {
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
use crate::core::{
|
use crate::core::{
|
||||||
web_socket::{DocumentWSSinkDataProvider, DocumentWSSteamConsumer, HttpWebSocketManager},
|
web_socket::{DocumentWSSinkDataProvider, DocumentWSSteamConsumer, HttpWebSocketManager},
|
||||||
DocumentRevisionManager,
|
DocumentRevisionManager, DocumentWSReceiver, DocumentWebSocket, EditorCommand, TransformDeltas,
|
||||||
DocumentWSReceiver,
|
|
||||||
DocumentWebSocket,
|
|
||||||
EditorCommand,
|
|
||||||
TransformDeltas,
|
|
||||||
};
|
};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use flowy_collaboration::{
|
use flowy_collaboration::{
|
||||||
@ -84,10 +80,10 @@ fn listen_document_ws_state(
|
|||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
while let Ok(state) = subscriber.recv().await {
|
while let Ok(state) = subscriber.recv().await {
|
||||||
match state {
|
match state {
|
||||||
WSConnectState::Init => {},
|
WSConnectState::Init => {}
|
||||||
WSConnectState::Connecting => {},
|
WSConnectState::Connecting => {}
|
||||||
WSConnectState::Connected => {},
|
WSConnectState::Connected => {}
|
||||||
WSConnectState::Disconnected => {},
|
WSConnectState::Disconnected => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -196,7 +192,7 @@ pub(crate) async fn handle_remote_revision(
|
|||||||
});
|
});
|
||||||
let _ = rx.await.map_err(internal_error)??;
|
let _ = rx.await.map_err(internal_error)??;
|
||||||
Ok(None)
|
Ok(None)
|
||||||
},
|
}
|
||||||
Some(server_prime) => {
|
Some(server_prime) => {
|
||||||
let (ret, rx) = oneshot::channel();
|
let (ret, rx) = oneshot::channel();
|
||||||
let _ = edit_cmd_tx.send(EditorCommand::ComposeRemoteDelta {
|
let _ = edit_cmd_tx.send(EditorCommand::ComposeRemoteDelta {
|
||||||
@ -206,7 +202,7 @@ pub(crate) async fn handle_remote_revision(
|
|||||||
ret,
|
ret,
|
||||||
});
|
});
|
||||||
Ok(rx.await.map_err(internal_error)??)
|
Ok(rx.await.map_err(internal_error)??)
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,9 +229,13 @@ impl SharedWSSinkDataProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) async fn push_front(&self, data: DocumentClientWSData) { self.shared.write().await.push_front(data); }
|
pub(crate) async fn push_front(&self, data: DocumentClientWSData) {
|
||||||
|
self.shared.write().await.push_front(data);
|
||||||
|
}
|
||||||
|
|
||||||
async fn push_back(&self, data: DocumentClientWSData) { self.shared.write().await.push_back(data); }
|
async fn push_back(&self, data: DocumentClientWSData) {
|
||||||
|
self.shared.write().await.push_back(data);
|
||||||
|
}
|
||||||
|
|
||||||
async fn next(&self) -> FlowyResult<Option<DocumentClientWSData>> {
|
async fn next(&self) -> FlowyResult<Option<DocumentClientWSData>> {
|
||||||
let source_ty = self.source_ty.read().await.clone();
|
let source_ty = self.source_ty.read().await.clone();
|
||||||
@ -244,11 +244,11 @@ impl SharedWSSinkDataProvider {
|
|||||||
None => {
|
None => {
|
||||||
*self.source_ty.write().await = SourceType::Revision;
|
*self.source_ty.write().await = SourceType::Revision;
|
||||||
Ok(None)
|
Ok(None)
|
||||||
},
|
}
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
tracing::debug!("[SharedWSSinkDataProvider]: {}:{:?}", data.doc_id, data.ty);
|
tracing::debug!("[SharedWSSinkDataProvider]: {}:{:?}", data.doc_id, data.ty);
|
||||||
Ok(Some(data.clone()))
|
Ok(Some(data.clone()))
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
SourceType::Revision => {
|
SourceType::Revision => {
|
||||||
if !self.shared.read().await.is_empty() {
|
if !self.shared.read().await.is_empty() {
|
||||||
@ -260,15 +260,15 @@ impl SharedWSSinkDataProvider {
|
|||||||
Some(rev) => {
|
Some(rev) => {
|
||||||
let doc_id = rev.doc_id.clone();
|
let doc_id = rev.doc_id.clone();
|
||||||
Ok(Some(DocumentClientWSData::from_revisions(&doc_id, vec![rev])))
|
Ok(Some(DocumentClientWSData::from_revisions(&doc_id, vec![rev])))
|
||||||
},
|
}
|
||||||
None => {
|
None => {
|
||||||
//
|
//
|
||||||
let doc_id = self.rev_manager.doc_id.clone();
|
let doc_id = self.rev_manager.doc_id.clone();
|
||||||
let latest_rev_id = self.rev_manager.rev_id();
|
let latest_rev_id = self.rev_manager.rev_id();
|
||||||
Ok(Some(DocumentClientWSData::ping(&doc_id, latest_rev_id)))
|
Ok(Some(DocumentClientWSData::ping(&doc_id, latest_rev_id)))
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,22 +287,22 @@ impl SharedWSSinkDataProvider {
|
|||||||
tracing::error!("The front element's {} is not equal to the {}", expected_id, id);
|
tracing::error!("The front element's {} is not equal to the {}", expected_id, id);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
if should_pop {
|
if should_pop {
|
||||||
let _ = self.shared.write().await.pop_front();
|
let _ = self.shared.write().await.pop_front();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
SourceType::Revision => {
|
SourceType::Revision => {
|
||||||
match id.parse::<i64>() {
|
match id.parse::<i64>() {
|
||||||
Ok(rev_id) => {
|
Ok(rev_id) => {
|
||||||
let _ = self.rev_manager.ack_revision(rev_id).await?;
|
let _ = self.rev_manager.ack_revision(rev_id).await?;
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Parse rev_id from {} failed. {}", id, e);
|
tracing::error!("Parse rev_id from {} failed. {}", id, e);
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -7,7 +7,9 @@ pub(crate) enum DocObservable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<DocObservable> for i32 {
|
impl std::convert::From<DocObservable> for i32 {
|
||||||
fn from(o: DocObservable) -> Self { o as i32 }
|
fn from(o: DocObservable) -> Self {
|
||||||
|
o as i32
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -14,14 +14,14 @@ impl ResponseMiddleware for DocMiddleware {
|
|||||||
log::error!("document user is unauthorized");
|
log::error!("document user is unauthorized");
|
||||||
|
|
||||||
match token {
|
match token {
|
||||||
None => {},
|
None => {}
|
||||||
Some(_token) => {
|
Some(_token) => {
|
||||||
// let error =
|
// let error =
|
||||||
// FlowyError::new(ErrorCode::UserUnauthorized, "");
|
// FlowyError::new(ErrorCode::UserUnauthorized, "");
|
||||||
// observable(token,
|
// observable(token,
|
||||||
// WorkspaceObservable::UserUnauthorized).error(error).
|
// WorkspaceObservable::UserUnauthorized).error(error).
|
||||||
// build()
|
// build()
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,9 @@ pub struct DocServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DocServer {
|
impl DocServer {
|
||||||
pub fn new(config: ClientServerConfiguration) -> Self { Self { config } }
|
pub fn new(config: ClientServerConfiguration) -> Self {
|
||||||
|
Self { config }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DocumentServerAPI for DocServer {
|
impl DocumentServerAPI for DocServer {
|
||||||
|
@ -29,7 +29,9 @@ impl std::default::Default for DocumentWSReceivers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DocumentWSReceivers {
|
impl DocumentWSReceivers {
|
||||||
pub fn new() -> Self { DocumentWSReceivers::default() }
|
pub fn new() -> Self {
|
||||||
|
DocumentWSReceivers::default()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn add(&self, doc_id: &str, receiver: Arc<dyn DocumentWSReceiver>) {
|
pub(crate) fn add(&self, doc_id: &str, receiver: Arc<dyn DocumentWSReceiver>) {
|
||||||
if self.receivers.contains_key(doc_id) {
|
if self.receivers.contains_key(doc_id) {
|
||||||
@ -38,23 +40,25 @@ impl DocumentWSReceivers {
|
|||||||
self.receivers.insert(doc_id.to_string(), receiver);
|
self.receivers.insert(doc_id.to_string(), receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn remove(&self, id: &str) { self.receivers.remove(id); }
|
pub(crate) fn remove(&self, id: &str) {
|
||||||
|
self.receivers.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn did_receive_data(&self, data: Bytes) {
|
pub fn did_receive_data(&self, data: Bytes) {
|
||||||
let data: DocumentServerWSData = data.try_into().unwrap();
|
let data: DocumentServerWSData = data.try_into().unwrap();
|
||||||
match self.receivers.get(&data.doc_id) {
|
match self.receivers.get(&data.doc_id) {
|
||||||
None => {
|
None => {
|
||||||
log::error!("Can't find any source handler for {:?}", data.doc_id);
|
log::error!("Can't find any source handler for {:?}", data.doc_id);
|
||||||
},
|
}
|
||||||
Some(handler) => {
|
Some(handler) => {
|
||||||
handler.receive_ws_data(data);
|
handler.receive_ws_data(data);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ws_connect_state_changed(&self, state: &WSConnectState) {
|
pub fn ws_connect_state_changed(&self, state: &WSConnectState) {
|
||||||
self.receivers.iter().for_each(|receiver| {
|
self.receivers.iter().for_each(|receiver| {
|
||||||
receiver.value().connect_state_changed(&state);
|
receiver.value().connect_state_changed(state);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,20 +46,20 @@ impl EditorTest {
|
|||||||
match script {
|
match script {
|
||||||
EditorScript::InsertText(s, offset) => {
|
EditorScript::InsertText(s, offset) => {
|
||||||
self.editor.insert(offset, s).await.unwrap();
|
self.editor.insert(offset, s).await.unwrap();
|
||||||
},
|
}
|
||||||
EditorScript::Delete(interval) => {
|
EditorScript::Delete(interval) => {
|
||||||
self.editor.delete(interval).await.unwrap();
|
self.editor.delete(interval).await.unwrap();
|
||||||
},
|
}
|
||||||
EditorScript::Replace(interval, s) => {
|
EditorScript::Replace(interval, s) => {
|
||||||
self.editor.replace(interval, s).await.unwrap();
|
self.editor.replace(interval, s).await.unwrap();
|
||||||
},
|
}
|
||||||
EditorScript::AssertRevisionState(rev_id, state) => {
|
EditorScript::AssertRevisionState(rev_id, state) => {
|
||||||
let record = cache.get(rev_id).await.unwrap();
|
let record = cache.get(rev_id).await.unwrap();
|
||||||
assert_eq!(record.state, state);
|
assert_eq!(record.state, state);
|
||||||
},
|
}
|
||||||
EditorScript::AssertCurrentRevId(rev_id) => {
|
EditorScript::AssertCurrentRevId(rev_id) => {
|
||||||
assert_eq!(self.editor.rev_manager().rev_id(), rev_id);
|
assert_eq!(self.editor.rev_manager().rev_id(), rev_id);
|
||||||
},
|
}
|
||||||
EditorScript::AssertNextRevId(rev_id) => {
|
EditorScript::AssertNextRevId(rev_id) => {
|
||||||
let next_revision = rev_manager.next_sync_revision().await.unwrap();
|
let next_revision = rev_manager.next_sync_revision().await.unwrap();
|
||||||
if rev_id.is_none() {
|
if rev_id.is_none() {
|
||||||
@ -68,7 +68,7 @@ impl EditorTest {
|
|||||||
}
|
}
|
||||||
let next_revision = next_revision.unwrap();
|
let next_revision = next_revision.unwrap();
|
||||||
assert_eq!(next_revision.rev_id, rev_id.unwrap());
|
assert_eq!(next_revision.rev_id, rev_id.unwrap());
|
||||||
},
|
}
|
||||||
EditorScript::AssertJson(expected) => {
|
EditorScript::AssertJson(expected) => {
|
||||||
let expected_delta: RichTextDelta = serde_json::from_str(expected).unwrap();
|
let expected_delta: RichTextDelta = serde_json::from_str(expected).unwrap();
|
||||||
let delta = self.editor.doc_delta().await.unwrap();
|
let delta = self.editor.doc_delta().await.unwrap();
|
||||||
@ -77,7 +77,7 @@ impl EditorTest {
|
|||||||
eprintln!("❌ receive: {}", delta.to_json());
|
eprintln!("❌ receive: {}", delta.to_json());
|
||||||
}
|
}
|
||||||
assert_eq!(expected_delta, delta);
|
assert_eq!(expected_delta, delta);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
sleep(Duration::from_millis(SYNC_INTERVAL_IN_MILLIS)).await;
|
sleep(Duration::from_millis(SYNC_INTERVAL_IN_MILLIS)).await;
|
||||||
}
|
}
|
||||||
|
@ -112,31 +112,31 @@ impl TestBuilder {
|
|||||||
tracing::debug!("Insert delta: {}", delta.to_json());
|
tracing::debug!("Insert delta: {}", delta.to_json());
|
||||||
|
|
||||||
self.deltas.insert(*delta_i, Some(delta));
|
self.deltas.insert(*delta_i, Some(delta));
|
||||||
},
|
}
|
||||||
TestOp::Delete(delta_i, iv) => {
|
TestOp::Delete(delta_i, iv) => {
|
||||||
let document = &mut self.documents[*delta_i];
|
let document = &mut self.documents[*delta_i];
|
||||||
let delta = document.replace(*iv, "").unwrap();
|
let delta = document.replace(*iv, "").unwrap();
|
||||||
tracing::trace!("Delete delta: {}", delta.to_json());
|
tracing::trace!("Delete delta: {}", delta.to_json());
|
||||||
self.deltas.insert(*delta_i, Some(delta));
|
self.deltas.insert(*delta_i, Some(delta));
|
||||||
},
|
}
|
||||||
TestOp::Replace(delta_i, iv, s) => {
|
TestOp::Replace(delta_i, iv, s) => {
|
||||||
let document = &mut self.documents[*delta_i];
|
let document = &mut self.documents[*delta_i];
|
||||||
let delta = document.replace(*iv, s).unwrap();
|
let delta = document.replace(*iv, s).unwrap();
|
||||||
tracing::trace!("Replace delta: {}", delta.to_json());
|
tracing::trace!("Replace delta: {}", delta.to_json());
|
||||||
self.deltas.insert(*delta_i, Some(delta));
|
self.deltas.insert(*delta_i, Some(delta));
|
||||||
},
|
}
|
||||||
TestOp::InsertBold(delta_i, s, iv) => {
|
TestOp::InsertBold(delta_i, s, iv) => {
|
||||||
let document = &mut self.documents[*delta_i];
|
let document = &mut self.documents[*delta_i];
|
||||||
document.insert(iv.start, s).unwrap();
|
document.insert(iv.start, s).unwrap();
|
||||||
document.format(*iv, RichTextAttribute::Bold(true)).unwrap();
|
document.format(*iv, RichTextAttribute::Bold(true)).unwrap();
|
||||||
},
|
}
|
||||||
TestOp::Bold(delta_i, iv, enable) => {
|
TestOp::Bold(delta_i, iv, enable) => {
|
||||||
let document = &mut self.documents[*delta_i];
|
let document = &mut self.documents[*delta_i];
|
||||||
let attribute = RichTextAttribute::Bold(*enable);
|
let attribute = RichTextAttribute::Bold(*enable);
|
||||||
let delta = document.format(*iv, attribute).unwrap();
|
let delta = document.format(*iv, attribute).unwrap();
|
||||||
tracing::trace!("Bold delta: {}", delta.to_json());
|
tracing::trace!("Bold delta: {}", delta.to_json());
|
||||||
self.deltas.insert(*delta_i, Some(delta));
|
self.deltas.insert(*delta_i, Some(delta));
|
||||||
},
|
}
|
||||||
TestOp::Italic(delta_i, iv, enable) => {
|
TestOp::Italic(delta_i, iv, enable) => {
|
||||||
let document = &mut self.documents[*delta_i];
|
let document = &mut self.documents[*delta_i];
|
||||||
let attribute = match *enable {
|
let attribute = match *enable {
|
||||||
@ -146,21 +146,21 @@ impl TestBuilder {
|
|||||||
let delta = document.format(*iv, attribute).unwrap();
|
let delta = document.format(*iv, attribute).unwrap();
|
||||||
tracing::trace!("Italic delta: {}", delta.to_json());
|
tracing::trace!("Italic delta: {}", delta.to_json());
|
||||||
self.deltas.insert(*delta_i, Some(delta));
|
self.deltas.insert(*delta_i, Some(delta));
|
||||||
},
|
}
|
||||||
TestOp::Header(delta_i, iv, level) => {
|
TestOp::Header(delta_i, iv, level) => {
|
||||||
let document = &mut self.documents[*delta_i];
|
let document = &mut self.documents[*delta_i];
|
||||||
let attribute = RichTextAttribute::Header(*level);
|
let attribute = RichTextAttribute::Header(*level);
|
||||||
let delta = document.format(*iv, attribute).unwrap();
|
let delta = document.format(*iv, attribute).unwrap();
|
||||||
tracing::trace!("Header delta: {}", delta.to_json());
|
tracing::trace!("Header delta: {}", delta.to_json());
|
||||||
self.deltas.insert(*delta_i, Some(delta));
|
self.deltas.insert(*delta_i, Some(delta));
|
||||||
},
|
}
|
||||||
TestOp::Link(delta_i, iv, link) => {
|
TestOp::Link(delta_i, iv, link) => {
|
||||||
let document = &mut self.documents[*delta_i];
|
let document = &mut self.documents[*delta_i];
|
||||||
let attribute = RichTextAttribute::Link(link.to_owned());
|
let attribute = RichTextAttribute::Link(link.to_owned());
|
||||||
let delta = document.format(*iv, attribute).unwrap();
|
let delta = document.format(*iv, attribute).unwrap();
|
||||||
tracing::trace!("Link delta: {}", delta.to_json());
|
tracing::trace!("Link delta: {}", delta.to_json());
|
||||||
self.deltas.insert(*delta_i, Some(delta));
|
self.deltas.insert(*delta_i, Some(delta));
|
||||||
},
|
}
|
||||||
TestOp::Bullet(delta_i, iv, enable) => {
|
TestOp::Bullet(delta_i, iv, enable) => {
|
||||||
let document = &mut self.documents[*delta_i];
|
let document = &mut self.documents[*delta_i];
|
||||||
let attribute = RichTextAttribute::Bullet(*enable);
|
let attribute = RichTextAttribute::Bullet(*enable);
|
||||||
@ -168,7 +168,7 @@ impl TestBuilder {
|
|||||||
tracing::debug!("Bullet delta: {}", delta.to_json());
|
tracing::debug!("Bullet delta: {}", delta.to_json());
|
||||||
|
|
||||||
self.deltas.insert(*delta_i, Some(delta));
|
self.deltas.insert(*delta_i, Some(delta));
|
||||||
},
|
}
|
||||||
TestOp::Transform(delta_a_i, delta_b_i) => {
|
TestOp::Transform(delta_a_i, delta_b_i) => {
|
||||||
let (a_prime, b_prime) = self.documents[*delta_a_i]
|
let (a_prime, b_prime) = self.documents[*delta_a_i]
|
||||||
.delta()
|
.delta()
|
||||||
@ -181,7 +181,7 @@ impl TestBuilder {
|
|||||||
|
|
||||||
self.documents[*delta_a_i].set_delta(data_left);
|
self.documents[*delta_a_i].set_delta(data_left);
|
||||||
self.documents[*delta_b_i].set_delta(data_right);
|
self.documents[*delta_b_i].set_delta(data_right);
|
||||||
},
|
}
|
||||||
TestOp::TransformPrime(a_doc_index, b_doc_index) => {
|
TestOp::TransformPrime(a_doc_index, b_doc_index) => {
|
||||||
let (prime_left, prime_right) = self.documents[*a_doc_index]
|
let (prime_left, prime_right) = self.documents[*a_doc_index]
|
||||||
.delta()
|
.delta()
|
||||||
@ -190,7 +190,7 @@ impl TestBuilder {
|
|||||||
|
|
||||||
self.primes.insert(*a_doc_index, Some(prime_left));
|
self.primes.insert(*a_doc_index, Some(prime_left));
|
||||||
self.primes.insert(*b_doc_index, Some(prime_right));
|
self.primes.insert(*b_doc_index, Some(prime_right));
|
||||||
},
|
}
|
||||||
TestOp::Invert(delta_a_i, delta_b_i) => {
|
TestOp::Invert(delta_a_i, delta_b_i) => {
|
||||||
let delta_a = &self.documents[*delta_a_i].delta();
|
let delta_a = &self.documents[*delta_a_i].delta();
|
||||||
let delta_b = &self.documents[*delta_b_i].delta();
|
let delta_b = &self.documents[*delta_b_i].delta();
|
||||||
@ -212,19 +212,19 @@ impl TestBuilder {
|
|||||||
assert_eq!(delta_a, &&new_delta_after_undo);
|
assert_eq!(delta_a, &&new_delta_after_undo);
|
||||||
|
|
||||||
self.documents[*delta_a_i].set_delta(new_delta_after_undo);
|
self.documents[*delta_a_i].set_delta(new_delta_after_undo);
|
||||||
},
|
}
|
||||||
TestOp::Undo(delta_i) => {
|
TestOp::Undo(delta_i) => {
|
||||||
self.documents[*delta_i].undo().unwrap();
|
self.documents[*delta_i].undo().unwrap();
|
||||||
},
|
}
|
||||||
TestOp::Redo(delta_i) => {
|
TestOp::Redo(delta_i) => {
|
||||||
self.documents[*delta_i].redo().unwrap();
|
self.documents[*delta_i].redo().unwrap();
|
||||||
},
|
}
|
||||||
TestOp::Wait(mills_sec) => {
|
TestOp::Wait(mills_sec) => {
|
||||||
std::thread::sleep(Duration::from_millis(*mills_sec as u64));
|
std::thread::sleep(Duration::from_millis(*mills_sec as u64));
|
||||||
},
|
}
|
||||||
TestOp::AssertStr(delta_i, expected) => {
|
TestOp::AssertStr(delta_i, expected) => {
|
||||||
assert_eq!(&self.documents[*delta_i].to_plain_string(), expected);
|
assert_eq!(&self.documents[*delta_i].to_plain_string(), expected);
|
||||||
},
|
}
|
||||||
|
|
||||||
TestOp::AssertDocJson(delta_i, expected) => {
|
TestOp::AssertDocJson(delta_i, expected) => {
|
||||||
let delta_json = self.documents[*delta_i].to_json();
|
let delta_json = self.documents[*delta_i].to_json();
|
||||||
@ -236,7 +236,7 @@ impl TestBuilder {
|
|||||||
log::error!("❌ receive: {}", delta_json);
|
log::error!("❌ receive: {}", delta_json);
|
||||||
}
|
}
|
||||||
assert_eq!(target_delta, expected_delta);
|
assert_eq!(target_delta, expected_delta);
|
||||||
},
|
}
|
||||||
|
|
||||||
TestOp::AssertPrimeJson(doc_i, expected) => {
|
TestOp::AssertPrimeJson(doc_i, expected) => {
|
||||||
let prime_json = self.primes[*doc_i].as_ref().unwrap().to_json();
|
let prime_json = self.primes[*doc_i].as_ref().unwrap().to_json();
|
||||||
@ -248,11 +248,11 @@ impl TestBuilder {
|
|||||||
log::error!("❌ receive prime: {}", prime_json);
|
log::error!("❌ receive prime: {}", prime_json);
|
||||||
}
|
}
|
||||||
assert_eq!(target_prime, expected_prime);
|
assert_eq!(target_prime, expected_prime);
|
||||||
},
|
}
|
||||||
TestOp::DocComposeDelta(doc_index, delta_i) => {
|
TestOp::DocComposeDelta(doc_index, delta_i) => {
|
||||||
let delta = self.deltas.get(*delta_i).unwrap().as_ref().unwrap();
|
let delta = self.deltas.get(*delta_i).unwrap().as_ref().unwrap();
|
||||||
self.documents[*doc_index].compose_delta(delta.clone()).unwrap();
|
self.documents[*doc_index].compose_delta(delta.clone()).unwrap();
|
||||||
},
|
}
|
||||||
TestOp::DocComposePrime(doc_index, prime_i) => {
|
TestOp::DocComposePrime(doc_index, prime_i) => {
|
||||||
let delta = self
|
let delta = self
|
||||||
.primes
|
.primes
|
||||||
@ -262,7 +262,7 @@ impl TestBuilder {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let new_delta = self.documents[*doc_index].delta().compose(delta).unwrap();
|
let new_delta = self.documents[*doc_index].delta().compose(delta).unwrap();
|
||||||
self.documents[*doc_index].set_delta(new_delta);
|
self.documents[*doc_index].set_delta(new_delta);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,12 +279,16 @@ impl TestBuilder {
|
|||||||
pub struct Rng(StdRng);
|
pub struct Rng(StdRng);
|
||||||
|
|
||||||
impl Default for Rng {
|
impl Default for Rng {
|
||||||
fn default() -> Self { Rng(StdRng::from_rng(thread_rng()).unwrap()) }
|
fn default() -> Self {
|
||||||
|
Rng(StdRng::from_rng(thread_rng()).unwrap())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rng {
|
impl Rng {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn from_seed(seed: [u8; 32]) -> Self { Rng(StdRng::from_seed(seed)) }
|
pub fn from_seed(seed: [u8; 32]) -> Self {
|
||||||
|
Rng(StdRng::from_seed(seed))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn gen_string(&mut self, len: usize) -> String {
|
pub fn gen_string(&mut self, len: usize) -> String {
|
||||||
(0..len)
|
(0..len)
|
||||||
@ -311,13 +315,13 @@ impl Rng {
|
|||||||
match self.0.gen_range(0.0, 1.0) {
|
match self.0.gen_range(0.0, 1.0) {
|
||||||
f if f < 0.2 => {
|
f if f < 0.2 => {
|
||||||
delta.insert(&self.gen_string(i), RichTextAttributes::default());
|
delta.insert(&self.gen_string(i), RichTextAttributes::default());
|
||||||
},
|
}
|
||||||
f if f < 0.4 => {
|
f if f < 0.4 => {
|
||||||
delta.delete(i);
|
delta.delete(i);
|
||||||
},
|
}
|
||||||
_ => {
|
_ => {
|
||||||
delta.retain(i, RichTextAttributes::default());
|
delta.retain(i, RichTextAttributes::default());
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.0.gen_range(0.0, 1.0) < 0.3 {
|
if self.0.gen_range(0.0, 1.0) < 0.3 {
|
||||||
|
@ -18,7 +18,9 @@ pub struct FlowyError {
|
|||||||
macro_rules! static_flowy_error {
|
macro_rules! static_flowy_error {
|
||||||
($name:ident, $code:expr) => {
|
($name:ident, $code:expr) => {
|
||||||
#[allow(non_snake_case, missing_docs)]
|
#[allow(non_snake_case, missing_docs)]
|
||||||
pub fn $name() -> FlowyError { $code.into() }
|
pub fn $name() -> FlowyError {
|
||||||
|
$code.into()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +83,9 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for FlowyError {
|
impl fmt::Display for FlowyError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}: {}", &self.code, &self.msg) }
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{:?}: {}", &self.code, &self.msg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl lib_dispatch::Error for FlowyError {
|
impl lib_dispatch::Error for FlowyError {
|
||||||
@ -92,9 +96,13 @@ impl lib_dispatch::Error for FlowyError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<std::io::Error> for FlowyError {
|
impl std::convert::From<std::io::Error> for FlowyError {
|
||||||
fn from(error: std::io::Error) -> Self { FlowyError::internal().context(error) }
|
fn from(error: std::io::Error) -> Self {
|
||||||
|
FlowyError::internal().context(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<protobuf::ProtobufError> for FlowyError {
|
impl std::convert::From<protobuf::ProtobufError> for FlowyError {
|
||||||
fn from(e: protobuf::ProtobufError) -> Self { FlowyError::internal().context(e) }
|
fn from(e: protobuf::ProtobufError) -> Self {
|
||||||
|
FlowyError::internal().context(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ fn server_error_to_flowy_error(code: ServerErrorCode) -> ErrorCode {
|
|||||||
ServerErrorCode::RecordNotFound => ErrorCode::RecordNotFound,
|
ServerErrorCode::RecordNotFound => ErrorCode::RecordNotFound,
|
||||||
ServerErrorCode::ConnectRefused | ServerErrorCode::ConnectTimeout | ServerErrorCode::ConnectClose => {
|
ServerErrorCode::ConnectRefused | ServerErrorCode::ConnectTimeout | ServerErrorCode::ConnectClose => {
|
||||||
ErrorCode::ConnectError
|
ErrorCode::ConnectError
|
||||||
},
|
}
|
||||||
_ => ErrorCode::Internal,
|
_ => ErrorCode::Internal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use crate::FlowyError;
|
use crate::FlowyError;
|
||||||
|
|
||||||
impl std::convert::From<flowy_collaboration::errors::CollaborateError> for FlowyError {
|
impl std::convert::From<flowy_collaboration::errors::CollaborateError> for FlowyError {
|
||||||
fn from(error: flowy_collaboration::errors::CollaborateError) -> Self { FlowyError::internal().context(error) }
|
fn from(error: flowy_collaboration::errors::CollaborateError) -> Self {
|
||||||
|
FlowyError::internal().context(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,21 @@
|
|||||||
use crate::FlowyError;
|
use crate::FlowyError;
|
||||||
|
|
||||||
impl std::convert::From<flowy_database::Error> for FlowyError {
|
impl std::convert::From<flowy_database::Error> for FlowyError {
|
||||||
fn from(error: flowy_database::Error) -> Self { FlowyError::internal().context(error) }
|
fn from(error: flowy_database::Error) -> Self {
|
||||||
|
FlowyError::internal().context(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<::r2d2::Error> for FlowyError {
|
impl std::convert::From<::r2d2::Error> for FlowyError {
|
||||||
fn from(error: r2d2::Error) -> Self { FlowyError::internal().context(error) }
|
fn from(error: r2d2::Error) -> Self {
|
||||||
|
FlowyError::internal().context(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// use diesel::result::{Error, DatabaseErrorKind};
|
// use diesel::result::{Error, DatabaseErrorKind};
|
||||||
// use lib_sqlite::ErrorKind;
|
// use lib_sqlite::ErrorKind;
|
||||||
impl std::convert::From<lib_sqlite::Error> for FlowyError {
|
impl std::convert::From<lib_sqlite::Error> for FlowyError {
|
||||||
fn from(error: lib_sqlite::Error) -> Self { FlowyError::internal().context(error) }
|
fn from(error: lib_sqlite::Error) -> Self {
|
||||||
|
FlowyError::internal().context(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use crate::FlowyError;
|
use crate::FlowyError;
|
||||||
|
|
||||||
impl std::convert::From<lib_ot::errors::OTError> for FlowyError {
|
impl std::convert::From<lib_ot::errors::OTError> for FlowyError {
|
||||||
fn from(error: lib_ot::errors::OTError) -> Self { FlowyError::internal().context(error) }
|
fn from(error: lib_ot::errors::OTError) -> Self {
|
||||||
|
FlowyError::internal().context(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use crate::FlowyError;
|
use crate::FlowyError;
|
||||||
|
|
||||||
impl std::convert::From<serde_json::Error> for FlowyError {
|
impl std::convert::From<serde_json::Error> for FlowyError {
|
||||||
fn from(error: serde_json::Error) -> Self { FlowyError::internal().context(error) }
|
fn from(error: serde_json::Error) -> Self {
|
||||||
|
FlowyError::internal().context(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,9 @@ use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
|||||||
#[derive(ProtoBuf_Enum, Debug, Clone, Eq, PartialEq)]
|
#[derive(ProtoBuf_Enum, Debug, Clone, Eq, PartialEq)]
|
||||||
pub enum NetworkType {
|
pub enum NetworkType {
|
||||||
UnknownNetworkType = 0,
|
UnknownNetworkType = 0,
|
||||||
Wifi = 1,
|
Wifi = 1,
|
||||||
Cell = 2,
|
Cell = 2,
|
||||||
Ethernet = 3,
|
Ethernet = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetworkType {
|
impl NetworkType {
|
||||||
@ -20,7 +20,9 @@ impl NetworkType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl std::default::Default for NetworkType {
|
impl std::default::Default for NetworkType {
|
||||||
fn default() -> Self { NetworkType::UnknownNetworkType }
|
fn default() -> Self {
|
||||||
|
NetworkType::UnknownNetworkType
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(ProtoBuf, Debug, Default, Clone)]
|
#[derive(ProtoBuf, Debug, Default, Clone)]
|
||||||
|
@ -9,7 +9,9 @@ use std::sync::Arc;
|
|||||||
use tokio::sync::broadcast::Receiver;
|
use tokio::sync::broadcast::Receiver;
|
||||||
|
|
||||||
impl FlowyRawWebSocket for Arc<WSController> {
|
impl FlowyRawWebSocket for Arc<WSController> {
|
||||||
fn initialize(&self) -> FutureResult<(), FlowyError> { FutureResult::new(async { Ok(()) }) }
|
fn initialize(&self) -> FutureResult<(), FlowyError> {
|
||||||
|
FutureResult::new(async { Ok(()) })
|
||||||
|
}
|
||||||
|
|
||||||
fn start_connect(&self, addr: String, _user_id: String) -> FutureResult<(), FlowyError> {
|
fn start_connect(&self, addr: String, _user_id: String) -> FutureResult<(), FlowyError> {
|
||||||
let cloned_ws = self.clone();
|
let cloned_ws = self.clone();
|
||||||
@ -27,7 +29,9 @@ impl FlowyRawWebSocket for Arc<WSController> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscribe_connect_state(&self) -> Receiver<WSConnectState> { self.subscribe_state() }
|
fn subscribe_connect_state(&self) -> Receiver<WSConnectState> {
|
||||||
|
self.subscribe_state()
|
||||||
|
}
|
||||||
|
|
||||||
fn reconnect(&self, count: usize) -> FutureResult<(), FlowyError> {
|
fn reconnect(&self, count: usize) -> FutureResult<(), FlowyError> {
|
||||||
let cloned_ws = self.clone();
|
let cloned_ws = self.clone();
|
||||||
|
@ -7,9 +7,7 @@ use flowy_collaboration::{
|
|||||||
},
|
},
|
||||||
errors::CollaborateError,
|
errors::CollaborateError,
|
||||||
protobuf::{
|
protobuf::{
|
||||||
DocumentClientWSData as DocumentClientWSDataPB,
|
DocumentClientWSData as DocumentClientWSDataPB, RepeatedRevision as RepeatedRevisionPB, Revision as RevisionPB,
|
||||||
RepeatedRevision as RepeatedRevisionPB,
|
|
||||||
Revision as RevisionPB,
|
|
||||||
},
|
},
|
||||||
sync::*,
|
sync::*,
|
||||||
util::repeated_revision_from_repeated_revision_pb,
|
util::repeated_revision_from_repeated_revision_pb,
|
||||||
@ -58,10 +56,10 @@ impl LocalDocumentServer {
|
|||||||
.doc_manager
|
.doc_manager
|
||||||
.handle_client_revisions(user, document_client_data)
|
.handle_client_revisions(user, document_client_data)
|
||||||
.await?;
|
.await?;
|
||||||
},
|
}
|
||||||
DocumentClientWSDataType::ClientPing => {
|
DocumentClientWSDataType::ClientPing => {
|
||||||
let _ = self.doc_manager.handle_client_ping(user, document_client_data).await?;
|
let _ = self.doc_manager.handle_client_ping(user, document_client_data).await?;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -72,7 +70,9 @@ struct LocalDocServerPersistence {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for LocalDocServerPersistence {
|
impl Debug for LocalDocServerPersistence {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str("LocalDocServerPersistence") }
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str("LocalDocServerPersistence")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::default::Default for LocalDocServerPersistence {
|
impl std::default::Default for LocalDocServerPersistence {
|
||||||
@ -93,7 +93,7 @@ impl DocumentPersistence for LocalDocServerPersistence {
|
|||||||
Some(val) => {
|
Some(val) => {
|
||||||
//
|
//
|
||||||
Ok(val.value().clone())
|
Ok(val.value().clone())
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -130,15 +130,17 @@ struct LocalDocumentUser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RevisionUser for LocalDocumentUser {
|
impl RevisionUser for LocalDocumentUser {
|
||||||
fn user_id(&self) -> String { self.user_id.clone() }
|
fn user_id(&self) -> String {
|
||||||
|
self.user_id.clone()
|
||||||
|
}
|
||||||
|
|
||||||
fn receive(&self, resp: SyncResponse) {
|
fn receive(&self, resp: SyncResponse) {
|
||||||
let sender = self.ws_sender.clone();
|
let sender = self.ws_sender.clone();
|
||||||
let send_fn = |sender: UnboundedSender<WebSocketRawMessage>, msg: WebSocketRawMessage| match sender.send(msg) {
|
let send_fn = |sender: UnboundedSender<WebSocketRawMessage>, msg: WebSocketRawMessage| match sender.send(msg) {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("LocalDocumentUser send message failed: {}", e);
|
tracing::error!("LocalDocumentUser send message failed: {}", e);
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
@ -150,7 +152,7 @@ impl RevisionUser for LocalDocumentUser {
|
|||||||
data: bytes.to_vec(),
|
data: bytes.to_vec(),
|
||||||
};
|
};
|
||||||
send_fn(sender, msg);
|
send_fn(sender, msg);
|
||||||
},
|
}
|
||||||
SyncResponse::Push(data) => {
|
SyncResponse::Push(data) => {
|
||||||
let bytes: Bytes = data.try_into().unwrap();
|
let bytes: Bytes = data.try_into().unwrap();
|
||||||
let msg = WebSocketRawMessage {
|
let msg = WebSocketRawMessage {
|
||||||
@ -158,7 +160,7 @@ impl RevisionUser for LocalDocumentUser {
|
|||||||
data: bytes.to_vec(),
|
data: bytes.to_vec(),
|
||||||
};
|
};
|
||||||
send_fn(sender, msg);
|
send_fn(sender, msg);
|
||||||
},
|
}
|
||||||
SyncResponse::Ack(data) => {
|
SyncResponse::Ack(data) => {
|
||||||
let bytes: Bytes = data.try_into().unwrap();
|
let bytes: Bytes = data.try_into().unwrap();
|
||||||
let msg = WebSocketRawMessage {
|
let msg = WebSocketRawMessage {
|
||||||
@ -166,10 +168,10 @@ impl RevisionUser for LocalDocumentUser {
|
|||||||
data: bytes.to_vec(),
|
data: bytes.to_vec(),
|
||||||
};
|
};
|
||||||
send_fn(sender, msg);
|
send_fn(sender, msg);
|
||||||
},
|
}
|
||||||
SyncResponse::NewRevision(mut _repeated_revision) => {
|
SyncResponse::NewRevision(mut _repeated_revision) => {
|
||||||
// unimplemented!()
|
// unimplemented!()
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -70,10 +70,10 @@ impl LocalWebSocket {
|
|||||||
Ok::<(), FlowyError>(())
|
Ok::<(), FlowyError>(())
|
||||||
};
|
};
|
||||||
match fut().await {
|
match fut().await {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => tracing::error!("[LocalWebSocket] error: {:?}", e),
|
Err(e) => tracing::error!("[LocalWebSocket] error: {:?}", e),
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(e) => tracing::error!("[LocalWebSocket] error: {}", e),
|
Err(e) => tracing::error!("[LocalWebSocket] error: {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,18 +102,26 @@ impl FlowyRawWebSocket for LocalWebSocket {
|
|||||||
FutureResult::new(async { Ok(()) })
|
FutureResult::new(async { Ok(()) })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop_connect(&self) -> FutureResult<(), FlowyError> { FutureResult::new(async { Ok(()) }) }
|
fn stop_connect(&self) -> FutureResult<(), FlowyError> {
|
||||||
|
FutureResult::new(async { Ok(()) })
|
||||||
|
}
|
||||||
|
|
||||||
fn subscribe_connect_state(&self) -> Receiver<WSConnectState> { self.state_sender.subscribe() }
|
fn subscribe_connect_state(&self) -> Receiver<WSConnectState> {
|
||||||
|
self.state_sender.subscribe()
|
||||||
|
}
|
||||||
|
|
||||||
fn reconnect(&self, _count: usize) -> FutureResult<(), FlowyError> { FutureResult::new(async { Ok(()) }) }
|
fn reconnect(&self, _count: usize) -> FutureResult<(), FlowyError> {
|
||||||
|
FutureResult::new(async { Ok(()) })
|
||||||
|
}
|
||||||
|
|
||||||
fn add_receiver(&self, receiver: Arc<dyn WSMessageReceiver>) -> Result<(), FlowyError> {
|
fn add_receiver(&self, receiver: Arc<dyn WSMessageReceiver>) -> Result<(), FlowyError> {
|
||||||
self.receivers.insert(receiver.source(), receiver);
|
self.receivers.insert(receiver.source(), receiver);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sender(&self) -> Result<Arc<dyn FlowyWSSender>, FlowyError> { Ok(Arc::new(self.ws_sender.clone())) }
|
fn sender(&self) -> Result<Arc<dyn FlowyWSSender>, FlowyError> {
|
||||||
|
Ok(Arc::new(self.ws_sender.clone()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -136,5 +144,7 @@ impl FlowyWSSender for LocalWSSender {
|
|||||||
impl std::ops::Deref for LocalWSSender {
|
impl std::ops::Deref for LocalWSSender {
|
||||||
type Target = broadcast::Sender<WebSocketRawMessage>;
|
type Target = broadcast::Sender<WebSocketRawMessage>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target { &self.0 }
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ impl FlowyWebSocketConnect {
|
|||||||
|
|
||||||
pub async fn init(&self) {
|
pub async fn init(&self) {
|
||||||
match self.inner.initialize().await {
|
match self.inner.initialize().await {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => tracing::error!("FlowyWebSocketConnect init error: {:?}", e),
|
Err(e) => tracing::error!("FlowyWebSocketConnect init error: {:?}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,7 +54,9 @@ impl FlowyWebSocketConnect {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn stop(&self) { let _ = self.inner.stop_connect().await; }
|
pub async fn stop(&self) {
|
||||||
|
let _ = self.inner.stop_connect().await;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_network_type(&self, new_type: &NetworkType) {
|
pub fn update_network_type(&self, new_type: &NetworkType) {
|
||||||
tracing::debug!("Network new state: {:?}", new_type);
|
tracing::debug!("Network new state: {:?}", new_type);
|
||||||
@ -67,11 +69,11 @@ impl FlowyWebSocketConnect {
|
|||||||
(false, true) => {
|
(false, true) => {
|
||||||
let ws_controller = self.inner.clone();
|
let ws_controller = self.inner.clone();
|
||||||
tokio::spawn(async move { retry_connect(ws_controller, 100).await });
|
tokio::spawn(async move { retry_connect(ws_controller, 100).await });
|
||||||
},
|
}
|
||||||
(true, false) => {
|
(true, false) => {
|
||||||
//
|
//
|
||||||
},
|
}
|
||||||
_ => {},
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
*self.connect_type.write() = new_type.clone();
|
*self.connect_type.write() = new_type.clone();
|
||||||
@ -82,14 +84,18 @@ impl FlowyWebSocketConnect {
|
|||||||
self.inner.subscribe_connect_state()
|
self.inner.subscribe_connect_state()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subscribe_network_ty(&self) -> broadcast::Receiver<NetworkType> { self.status_notifier.subscribe() }
|
pub fn subscribe_network_ty(&self) -> broadcast::Receiver<NetworkType> {
|
||||||
|
self.status_notifier.subscribe()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_ws_message_receiver(&self, receiver: Arc<dyn WSMessageReceiver>) -> Result<(), FlowyError> {
|
pub fn add_ws_message_receiver(&self, receiver: Arc<dyn WSMessageReceiver>) -> Result<(), FlowyError> {
|
||||||
let _ = self.inner.add_receiver(receiver)?;
|
let _ = self.inner.add_receiver(receiver)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ws_sender(&self) -> Result<Arc<dyn FlowyWSSender>, FlowyError> { self.inner.sender() }
|
pub fn ws_sender(&self) -> Result<Arc<dyn FlowyWSSender>, FlowyError> {
|
||||||
|
self.inner.sender()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(ws_conn))]
|
#[tracing::instrument(level = "debug", skip(ws_conn))]
|
||||||
@ -103,16 +109,16 @@ pub fn listen_on_websocket(ws_conn: Arc<FlowyWebSocketConnect>) {
|
|||||||
Ok(state) => {
|
Ok(state) => {
|
||||||
tracing::info!("Websocket state changed: {}", state);
|
tracing::info!("Websocket state changed: {}", state);
|
||||||
match state {
|
match state {
|
||||||
WSConnectState::Init => {},
|
WSConnectState::Init => {}
|
||||||
WSConnectState::Connected => {},
|
WSConnectState::Connected => {}
|
||||||
WSConnectState::Connecting => {},
|
WSConnectState::Connecting => {}
|
||||||
WSConnectState::Disconnected => retry_connect(ws.clone(), 100).await,
|
WSConnectState::Disconnected => retry_connect(ws.clone(), 100).await,
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Websocket state notify error: {:?}", e);
|
tracing::error!("Websocket state notify error: {:?}", e);
|
||||||
break;
|
break;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -123,9 +129,9 @@ pub fn listen_on_websocket(ws_conn: Arc<FlowyWebSocketConnect>) {
|
|||||||
|
|
||||||
async fn retry_connect(ws: Arc<dyn FlowyRawWebSocket>, count: usize) {
|
async fn retry_connect(ws: Arc<dyn FlowyRawWebSocket>, count: usize) {
|
||||||
match ws.reconnect(count).await {
|
match ws.reconnect(count).await {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("websocket connect failed: {:?}", e);
|
tracing::error!("websocket connect failed: {:?}", e);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,11 +53,17 @@ impl DocumentUser for DocumentUserImpl {
|
|||||||
Ok(doc_dir)
|
Ok(doc_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn user_id(&self) -> Result<String, FlowyError> { self.user.user_id() }
|
fn user_id(&self) -> Result<String, FlowyError> {
|
||||||
|
self.user.user_id()
|
||||||
|
}
|
||||||
|
|
||||||
fn token(&self) -> Result<String, FlowyError> { self.user.token() }
|
fn token(&self) -> Result<String, FlowyError> {
|
||||||
|
self.user.token()
|
||||||
|
}
|
||||||
|
|
||||||
fn db_pool(&self) -> Result<Arc<ConnectionPool>, FlowyError> { self.user.db_pool() }
|
fn db_pool(&self) -> Result<Arc<ConnectionPool>, FlowyError> {
|
||||||
|
self.user.db_pool()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DocumentWebSocketAdapter {
|
struct DocumentWebSocketAdapter {
|
||||||
@ -76,12 +82,18 @@ impl DocumentWebSocket for DocumentWebSocketAdapter {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscribe_state_changed(&self) -> WSStateReceiver { self.ws_conn.subscribe_websocket_state() }
|
fn subscribe_state_changed(&self) -> WSStateReceiver {
|
||||||
|
self.ws_conn.subscribe_websocket_state()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WSMessageReceiverAdaptor(Arc<DocumentWSReceivers>);
|
struct WSMessageReceiverAdaptor(Arc<DocumentWSReceivers>);
|
||||||
|
|
||||||
impl WSMessageReceiver for WSMessageReceiverAdaptor {
|
impl WSMessageReceiver for WSMessageReceiverAdaptor {
|
||||||
fn source(&self) -> WSModule { WSModule::Doc }
|
fn source(&self) -> WSModule {
|
||||||
fn receive_message(&self, msg: WebSocketRawMessage) { self.0.did_receive_data(Bytes::from(msg.data)); }
|
WSModule::Doc
|
||||||
|
}
|
||||||
|
fn receive_message(&self, msg: WebSocketRawMessage) {
|
||||||
|
self.0.did_receive_data(Bytes::from(msg.data));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,9 @@ impl FlowySDK {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatcher(&self) -> Arc<EventDispatcher> { self.dispatcher.clone() }
|
pub fn dispatcher(&self) -> Arc<EventDispatcher> {
|
||||||
|
self.dispatcher.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _init(
|
fn _init(
|
||||||
@ -161,26 +163,26 @@ async fn _listen_user_status(
|
|||||||
UserStatus::Login { token, user_id } => {
|
UserStatus::Login { token, user_id } => {
|
||||||
let _ = core.user_did_sign_in(&token).await?;
|
let _ = core.user_did_sign_in(&token).await?;
|
||||||
let _ = ws_conn.start(token, user_id).await?;
|
let _ = ws_conn.start(token, user_id).await?;
|
||||||
},
|
}
|
||||||
UserStatus::Logout { .. } => {
|
UserStatus::Logout { .. } => {
|
||||||
core.user_did_logout().await;
|
core.user_did_logout().await;
|
||||||
let _ = ws_conn.stop().await;
|
let _ = ws_conn.stop().await;
|
||||||
},
|
}
|
||||||
UserStatus::Expired { .. } => {
|
UserStatus::Expired { .. } => {
|
||||||
core.user_session_expired().await;
|
core.user_session_expired().await;
|
||||||
let _ = ws_conn.stop().await;
|
let _ = ws_conn.stop().await;
|
||||||
},
|
}
|
||||||
UserStatus::SignUp { profile, ret } => {
|
UserStatus::SignUp { profile, ret } => {
|
||||||
let _ = core.user_did_sign_up(&profile.token).await?;
|
let _ = core.user_did_sign_up(&profile.token).await?;
|
||||||
let _ = ws_conn.start(profile.token.clone(), profile.id.clone()).await?;
|
let _ = ws_conn.start(profile.token.clone(), profile.id.clone()).await?;
|
||||||
let _ = ret.send(());
|
let _ = ret.send(());
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
Ok::<(), FlowyError>(())
|
Ok::<(), FlowyError>(())
|
||||||
};
|
};
|
||||||
|
|
||||||
match result().await {
|
match result().await {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => log::error!("{}", e),
|
Err(e) => log::error!("{}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -194,7 +196,7 @@ async fn _listen_network_status(mut subscribe: broadcast::Receiver<NetworkType>,
|
|||||||
|
|
||||||
fn init_kv(root: &str) {
|
fn init_kv(root: &str) {
|
||||||
match flowy_database::kv::KV::init(root) {
|
match flowy_database::kv::KV::init(root) {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => tracing::error!("Init kv store failedL: {}", e),
|
Err(e) => tracing::error!("Init kv store failedL: {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,14 @@ pub fn mk_modules(
|
|||||||
vec![user_module, core_module, network_module]
|
vec![user_module, core_module, network_module]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mk_user_module(user_session: Arc<UserSession>) -> Module { flowy_user::module::create(user_session) }
|
fn mk_user_module(user_session: Arc<UserSession>) -> Module {
|
||||||
|
flowy_user::module::create(user_session)
|
||||||
|
}
|
||||||
|
|
||||||
fn mk_core_module(core: Arc<CoreContext>) -> Module { flowy_core::module::create(core) }
|
fn mk_core_module(core: Arc<CoreContext>) -> Module {
|
||||||
|
flowy_core::module::create(core)
|
||||||
|
}
|
||||||
|
|
||||||
fn mk_network_module(ws_conn: Arc<FlowyWebSocketConnect>) -> Module { flowy_net::module::create(ws_conn) }
|
fn mk_network_module(ws_conn: Arc<FlowyWebSocketConnect>) -> Module {
|
||||||
|
flowy_net::module::create(ws_conn)
|
||||||
|
}
|
||||||
|
@ -11,8 +11,12 @@ use std::{
|
|||||||
|
|
||||||
pub type CoreModuleEventBuilder = EventBuilder<FlowyError>;
|
pub type CoreModuleEventBuilder = EventBuilder<FlowyError>;
|
||||||
impl CoreModuleEventBuilder {
|
impl CoreModuleEventBuilder {
|
||||||
pub fn new(sdk: FlowySDKTest) -> Self { EventBuilder::test(TestContext::new(sdk)) }
|
pub fn new(sdk: FlowySDKTest) -> Self {
|
||||||
pub fn user_profile(&self) -> &Option<UserProfile> { &self.user_profile }
|
EventBuilder::test(TestContext::new(sdk))
|
||||||
|
}
|
||||||
|
pub fn user_profile(&self) -> &Option<UserProfile> {
|
||||||
|
&self.user_profile
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type UserModuleEventBuilder = CoreModuleEventBuilder;
|
pub type UserModuleEventBuilder = CoreModuleEventBuilder;
|
||||||
@ -44,10 +48,10 @@ where
|
|||||||
Ok(bytes) => {
|
Ok(bytes) => {
|
||||||
let module_request = self.get_request();
|
let module_request = self.get_request();
|
||||||
self.context.request = Some(module_request.payload(bytes))
|
self.context.request = Some(module_request.payload(bytes))
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Set payload failed: {:?}", e);
|
log::error!("Set payload failed: {:?}", e);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -83,7 +87,7 @@ where
|
|||||||
Ok(Ok(data)) => data,
|
Ok(Ok(data)) => data,
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
panic!("parse failed: {:?}", e)
|
panic!("parse failed: {:?}", e)
|
||||||
},
|
}
|
||||||
Err(e) => panic!("Internal error: {:?}", e),
|
Err(e) => panic!("Internal error: {:?}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,7 +108,9 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch(&self) -> Arc<EventDispatcher> { self.context.sdk.dispatcher() }
|
fn dispatch(&self) -> Arc<EventDispatcher> {
|
||||||
|
self.context.sdk.dispatcher()
|
||||||
|
}
|
||||||
|
|
||||||
fn get_response(&self) -> EventResponse {
|
fn get_response(&self) -> EventResponse {
|
||||||
self.context
|
self.context
|
||||||
@ -114,7 +120,9 @@ where
|
|||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_request(&mut self) -> ModuleRequest { self.context.request.take().expect("must call event first") }
|
fn get_request(&mut self) -> ModuleRequest {
|
||||||
|
self.context.request.take().expect("must call event first")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -72,10 +72,10 @@ pub struct ViewTest {
|
|||||||
|
|
||||||
impl ViewTest {
|
impl ViewTest {
|
||||||
pub async fn new(sdk: &FlowySDKTest) -> Self {
|
pub async fn new(sdk: &FlowySDKTest) -> Self {
|
||||||
let workspace = create_workspace(&sdk, "Workspace", "").await;
|
let workspace = create_workspace(sdk, "Workspace", "").await;
|
||||||
open_workspace(&sdk, &workspace.id).await;
|
open_workspace(sdk, &workspace.id).await;
|
||||||
let app = create_app(&sdk, "App", "AppFlowy GitHub Project", &workspace.id).await;
|
let app = create_app(sdk, "App", "AppFlowy GitHub Project", &workspace.id).await;
|
||||||
let view = create_view(&sdk, &app.id).await;
|
let view = create_view(sdk, &app.id).await;
|
||||||
Self {
|
Self {
|
||||||
sdk: sdk.clone(),
|
sdk: sdk.clone(),
|
||||||
workspace,
|
workspace,
|
||||||
@ -291,11 +291,17 @@ pub fn root_dir() -> String {
|
|||||||
root_dir
|
root_dir
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn random_email() -> String { format!("{}@appflowy.io", uuid_string()) }
|
pub fn random_email() -> String {
|
||||||
|
format!("{}@appflowy.io", uuid_string())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn login_email() -> String { "annie2@appflowy.io".to_string() }
|
pub fn login_email() -> String {
|
||||||
|
"annie2@appflowy.io".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn login_password() -> String { "HelloWorld!123".to_string() }
|
pub fn login_password() -> String {
|
||||||
|
"HelloWorld!123".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
pub struct SignUpContext {
|
pub struct SignUpContext {
|
||||||
pub user_profile: UserProfile,
|
pub user_profile: UserProfile,
|
||||||
@ -365,4 +371,6 @@ fn sign_in(dispatch: Arc<EventDispatcher>) -> UserProfile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn logout(dispatch: Arc<EventDispatcher>) { let _ = EventDispatcher::sync_send(dispatch, ModuleRequest::new(SignOut)); }
|
fn logout(dispatch: Arc<EventDispatcher>) {
|
||||||
|
let _ = EventDispatcher::sync_send(dispatch, ModuleRequest::new(SignOut));
|
||||||
|
}
|
||||||
|
@ -20,7 +20,9 @@ pub struct FlowySDKTest {
|
|||||||
impl std::ops::Deref for FlowySDKTest {
|
impl std::ops::Deref for FlowySDKTest {
|
||||||
type Target = FlowySDK;
|
type Target = FlowySDK;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target { &self.inner }
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::default::Default for FlowySDKTest {
|
impl std::default::Default for FlowySDKTest {
|
||||||
|
@ -5,23 +5,23 @@ use strum_macros::Display;
|
|||||||
#[event_err = "FlowyError"]
|
#[event_err = "FlowyError"]
|
||||||
pub enum UserEvent {
|
pub enum UserEvent {
|
||||||
#[event()]
|
#[event()]
|
||||||
InitUser = 0,
|
InitUser = 0,
|
||||||
|
|
||||||
#[event(input = "SignInRequest", output = "UserProfile")]
|
#[event(input = "SignInRequest", output = "UserProfile")]
|
||||||
SignIn = 1,
|
SignIn = 1,
|
||||||
|
|
||||||
#[event(input = "SignUpRequest", output = "UserProfile")]
|
#[event(input = "SignUpRequest", output = "UserProfile")]
|
||||||
SignUp = 2,
|
SignUp = 2,
|
||||||
|
|
||||||
#[event(passthrough)]
|
#[event(passthrough)]
|
||||||
SignOut = 3,
|
SignOut = 3,
|
||||||
|
|
||||||
#[event(input = "UpdateUserRequest")]
|
#[event(input = "UpdateUserRequest")]
|
||||||
UpdateUser = 4,
|
UpdateUser = 4,
|
||||||
|
|
||||||
#[event(output = "UserProfile")]
|
#[event(output = "UserProfile")]
|
||||||
GetUserProfile = 5,
|
GetUserProfile = 5,
|
||||||
|
|
||||||
#[event(output = "UserProfile")]
|
#[event(output = "UserProfile")]
|
||||||
CheckUser = 6,
|
CheckUser = 6,
|
||||||
}
|
}
|
||||||
|
@ -4,19 +4,23 @@ const OBSERVABLE_CATEGORY: &str = "User";
|
|||||||
|
|
||||||
#[derive(ProtoBuf_Enum, Debug)]
|
#[derive(ProtoBuf_Enum, Debug)]
|
||||||
pub(crate) enum UserNotification {
|
pub(crate) enum UserNotification {
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
UserAuthChanged = 1,
|
UserAuthChanged = 1,
|
||||||
UserProfileUpdated = 2,
|
UserProfileUpdated = 2,
|
||||||
UserUnauthorized = 3,
|
UserUnauthorized = 3,
|
||||||
UserWsConnectStateChanged = 4,
|
UserWsConnectStateChanged = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::default::Default for UserNotification {
|
impl std::default::Default for UserNotification {
|
||||||
fn default() -> Self { UserNotification::Unknown }
|
fn default() -> Self {
|
||||||
|
UserNotification::Unknown
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<UserNotification> for i32 {
|
impl std::convert::From<UserNotification> for i32 {
|
||||||
fn from(notification: UserNotification) -> Self { notification as i32 }
|
fn from(notification: UserNotification) -> Self {
|
||||||
|
notification as i32
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn dart_notify(id: &str, ty: UserNotification) -> DartNotifyBuilder {
|
pub(crate) fn dart_notify(id: &str, ty: UserNotification) -> DartNotifyBuilder {
|
||||||
|
@ -10,7 +10,9 @@ pub struct UserHttpServer {
|
|||||||
config: ClientServerConfiguration,
|
config: ClientServerConfiguration,
|
||||||
}
|
}
|
||||||
impl UserHttpServer {
|
impl UserHttpServer {
|
||||||
pub fn new(config: ClientServerConfiguration) -> Self { Self { config } }
|
pub fn new(config: ClientServerConfiguration) -> Self {
|
||||||
|
Self { config }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserServerAPI for UserHttpServer {
|
impl UserServerAPI for UserHttpServer {
|
||||||
@ -57,7 +59,9 @@ impl UserServerAPI for UserHttpServer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ws_addr(&self) -> String { self.config.ws_addr() }
|
fn ws_addr(&self) -> String {
|
||||||
|
self.config.ws_addr()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// use crate::notify::*;
|
// use crate::notify::*;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user