mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
rm unuse code & rename folder -> client_folder
This commit is contained in:
parent
f90e3b63cd
commit
d97abcc99f
@ -135,7 +135,7 @@ fn user_scope() -> Scope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn init_app_context(configuration: &Settings) -> AppContext {
|
pub async fn init_app_context(configuration: &Settings) -> AppContext {
|
||||||
let level = std::env::var("RUST_LOG").unwrap_or("info".to_owned());
|
let level = std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_owned());
|
||||||
let _ = crate::services::log::Builder::new("flowy-server")
|
let _ = crate::services::log::Builder::new("flowy-server")
|
||||||
.env_filter(&level)
|
.env_filter(&level)
|
||||||
.build();
|
.build();
|
||||||
|
@ -15,7 +15,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
|
|||||||
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
|
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
|
||||||
CARGO_MAKE_CRATE_NAME = "dart-ffi"
|
CARGO_MAKE_CRATE_NAME = "dart-ffi"
|
||||||
VERSION = "0.0.2"
|
VERSION = "0.0.2"
|
||||||
FEATURES = "flutter"
|
FEATURES = "flutter,http_server"
|
||||||
PRODUCT_NAME = "AppFlowy"
|
PRODUCT_NAME = "AppFlowy"
|
||||||
#CRATE_TYPE: https://doc.rust-lang.org/reference/linkage.html
|
#CRATE_TYPE: https://doc.rust-lang.org/reference/linkage.html
|
||||||
CRATE_TYPE = "staticlib"
|
CRATE_TYPE = "staticlib"
|
||||||
|
@ -5,7 +5,7 @@ use flowy_core_data_model::user_default;
|
|||||||
use flowy_sync::RevisionWebSocket;
|
use flowy_sync::RevisionWebSocket;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
use flowy_collaboration::{entities::ws_data::ServerRevisionWSData, folder::FolderPad};
|
use flowy_collaboration::{client_folder::FolderPad, entities::ws_data::ServerRevisionWSData};
|
||||||
use flowy_document::FlowyDocumentManager;
|
use flowy_document::FlowyDocumentManager;
|
||||||
|
|
||||||
use std::{collections::HashMap, convert::TryInto, fmt::Formatter, sync::Arc};
|
use std::{collections::HashMap, convert::TryInto, fmt::Formatter, sync::Arc};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::services::web_socket::make_folder_ws_manager;
|
use crate::services::web_socket::make_folder_ws_manager;
|
||||||
use flowy_collaboration::{
|
use flowy_collaboration::{
|
||||||
|
client_folder::{FolderChange, FolderPad},
|
||||||
entities::{revision::Revision, ws_data::ServerRevisionWSData},
|
entities::{revision::Revision, ws_data::ServerRevisionWSData},
|
||||||
folder::{FolderChange, FolderPad},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::controller::FolderId;
|
use crate::controller::FolderId;
|
||||||
|
@ -2,7 +2,7 @@ use crate::{
|
|||||||
module::WorkspaceDatabase,
|
module::WorkspaceDatabase,
|
||||||
services::persistence::{AppTableSql, TrashTableSql, ViewTableSql, WorkspaceTableSql},
|
services::persistence::{AppTableSql, TrashTableSql, ViewTableSql, WorkspaceTableSql},
|
||||||
};
|
};
|
||||||
use flowy_collaboration::{entities::revision::md5, folder::FolderPad};
|
use flowy_collaboration::{client_folder::FolderPad, entities::revision::md5};
|
||||||
use flowy_core_data_model::entities::{
|
use flowy_core_data_model::entities::{
|
||||||
app::{App, RepeatedApp},
|
app::{App, RepeatedApp},
|
||||||
view::{RepeatedView, View},
|
view::{RepeatedView, View},
|
||||||
|
@ -3,8 +3,8 @@ pub mod version_1;
|
|||||||
mod version_2;
|
mod version_2;
|
||||||
|
|
||||||
use flowy_collaboration::{
|
use flowy_collaboration::{
|
||||||
|
client_folder::FolderPad,
|
||||||
entities::revision::{Revision, RevisionState},
|
entities::revision::{Revision, RevisionState},
|
||||||
folder::FolderPad,
|
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use crate::services::FOLDER_SYNC_INTERVAL_IN_MILLIS;
|
use crate::services::FOLDER_SYNC_INTERVAL_IN_MILLIS;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use flowy_collaboration::{
|
use flowy_collaboration::{
|
||||||
|
client_folder::FolderPad,
|
||||||
entities::{
|
entities::{
|
||||||
revision::RevisionRange,
|
revision::RevisionRange,
|
||||||
ws_data::{ClientRevisionWSData, NewDocumentUser, ServerRevisionWSDataType},
|
ws_data::{ClientRevisionWSData, NewDocumentUser, ServerRevisionWSDataType},
|
||||||
},
|
},
|
||||||
folder::FolderPad,
|
|
||||||
};
|
};
|
||||||
use flowy_error::FlowyError;
|
use flowy_error::FlowyError;
|
||||||
use flowy_sync::*;
|
use flowy_sync::*;
|
||||||
|
@ -200,17 +200,19 @@ impl RevisionWSStream {
|
|||||||
async fn handle_message(&self, msg: ServerRevisionWSData) -> FlowyResult<()> {
|
async fn handle_message(&self, msg: ServerRevisionWSData) -> FlowyResult<()> {
|
||||||
let ServerRevisionWSData { object_id, ty, data } = msg;
|
let ServerRevisionWSData { object_id, ty, data } = msg;
|
||||||
let bytes = Bytes::from(data);
|
let bytes = Bytes::from(data);
|
||||||
tracing::trace!("[{}]: new message: {}:{:?}", self, object_id, ty);
|
|
||||||
match ty {
|
match ty {
|
||||||
ServerRevisionWSDataType::ServerPushRev => {
|
ServerRevisionWSDataType::ServerPushRev => {
|
||||||
|
tracing::trace!("[{}]: new push revision: {}:{:?}", self, object_id, ty);
|
||||||
let _ = self.consumer.receive_push_revision(bytes).await?;
|
let _ = self.consumer.receive_push_revision(bytes).await?;
|
||||||
}
|
}
|
||||||
ServerRevisionWSDataType::ServerPullRev => {
|
ServerRevisionWSDataType::ServerPullRev => {
|
||||||
let range = RevisionRange::try_from(bytes)?;
|
let range = RevisionRange::try_from(bytes)?;
|
||||||
|
tracing::trace!("[{}]: new pull: {}:{}-{:?}", self, object_id, range, ty);
|
||||||
let _ = self.consumer.pull_revisions_in_range(range).await?;
|
let _ = self.consumer.pull_revisions_in_range(range).await?;
|
||||||
}
|
}
|
||||||
ServerRevisionWSDataType::ServerAck => {
|
ServerRevisionWSDataType::ServerAck => {
|
||||||
let rev_id = RevId::try_from(bytes).unwrap().value;
|
let rev_id = RevId::try_from(bytes).unwrap().value;
|
||||||
|
tracing::trace!("[{}]: new ack: {}:{}-{:?}", self, object_id, rev_id, ty);
|
||||||
let _ = self.consumer.receive_ack(rev_id.to_string(), ty).await;
|
let _ = self.consumer.receive_ack(rev_id.to_string(), ty).await;
|
||||||
}
|
}
|
||||||
ServerRevisionWSDataType::UserConnect => {
|
ServerRevisionWSDataType::UserConnect => {
|
||||||
|
@ -27,5 +27,3 @@ parking_lot = "0.11"
|
|||||||
dashmap = "4.0"
|
dashmap = "4.0"
|
||||||
futures = "0.3.15"
|
futures = "0.3.15"
|
||||||
async-stream = "0.3.2"
|
async-stream = "0.3.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
|
client_folder::{default_folder_delta, FolderPad},
|
||||||
entities::revision::Revision,
|
entities::revision::Revision,
|
||||||
errors::{CollaborateError, CollaborateResult},
|
errors::{CollaborateError, CollaborateResult},
|
||||||
folder::{default_folder_delta, FolderPad},
|
|
||||||
};
|
};
|
||||||
use flowy_core_data_model::entities::{trash::Trash, workspace::Workspace};
|
use flowy_core_data_model::entities::{trash::Trash, workspace::Workspace};
|
||||||
use lib_ot::core::{OperationTransformable, PlainDelta, PlainDeltaBuilder};
|
use lib_ot::core::{OperationTransformable, PlainDelta, PlainDeltaBuilder};
|
@ -1,10 +1,10 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
|
client_folder::builder::FolderPadBuilder,
|
||||||
entities::{
|
entities::{
|
||||||
folder_info::FolderDelta,
|
folder_info::FolderDelta,
|
||||||
revision::{md5, Revision},
|
revision::{md5, Revision},
|
||||||
},
|
},
|
||||||
errors::{CollaborateError, CollaborateResult},
|
errors::{CollaborateError, CollaborateResult},
|
||||||
folder::builder::FolderPadBuilder,
|
|
||||||
};
|
};
|
||||||
use dissimilar::*;
|
use dissimilar::*;
|
||||||
use flowy_core_data_model::entities::{app::App, trash::Trash, view::View, workspace::Workspace};
|
use flowy_core_data_model::entities::{app::App, trash::Trash, view::View, workspace::Workspace};
|
||||||
@ -401,7 +401,7 @@ fn cal_diff(old: String, new: String) -> Delta<PlainTextAttributes> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#![allow(clippy::all)]
|
#![allow(clippy::all)]
|
||||||
use crate::{entities::folder_info::FolderDelta, folder::folder_pad::FolderPad};
|
use crate::{client_folder::folder_pad::FolderPad, entities::folder_info::FolderDelta};
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use flowy_core_data_model::entities::{app::App, trash::Trash, view::View, workspace::Workspace};
|
use flowy_core_data_model::entities::{app::App, trash::Trash, view::View, workspace::Workspace};
|
||||||
use lib_ot::core::{OperationTransformable, PlainDelta, PlainDeltaBuilder};
|
use lib_ot::core::{OperationTransformable, PlainDelta, PlainDeltaBuilder};
|
@ -182,6 +182,12 @@ pub struct RevisionRange {
|
|||||||
pub end: i64,
|
pub end: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for RevisionRange {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_fmt(format_args!("[{},{}]", self.start, self.end))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl RevisionRange {
|
impl RevisionRange {
|
||||||
pub fn len(&self) -> i64 {
|
pub fn len(&self) -> i64 {
|
||||||
debug_assert!(self.end >= self.start);
|
debug_assert!(self.end >= self.start);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
pub mod client_document;
|
pub mod client_document;
|
||||||
|
pub mod client_folder;
|
||||||
pub mod entities;
|
pub mod entities;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod folder;
|
|
||||||
pub mod protobuf;
|
pub mod protobuf;
|
||||||
pub mod server_document;
|
pub mod server_document;
|
||||||
pub mod server_folder;
|
pub mod server_folder;
|
||||||
|
@ -1,243 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
document::Document,
|
|
||||||
entities::{
|
|
||||||
revision::RevisionRange,
|
|
||||||
ws::{DocumentServerWSData, DocumentServerWSDataBuilder},
|
|
||||||
},
|
|
||||||
errors::CollaborateError,
|
|
||||||
protobuf::{RepeatedRevision as RepeatedRevisionPB, Revision as RevisionPB},
|
|
||||||
sync::DocumentPersistence,
|
|
||||||
util::*,
|
|
||||||
};
|
|
||||||
use lib_ot::{core::OperationTransformable, rich_text::RichTextDelta};
|
|
||||||
use parking_lot::RwLock;
|
|
||||||
use std::{
|
|
||||||
cmp::Ordering,
|
|
||||||
fmt::Debug,
|
|
||||||
sync::{
|
|
||||||
atomic::{AtomicI64, Ordering::SeqCst},
|
|
||||||
Arc,
|
|
||||||
},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub trait RevisionUser: Send + Sync + Debug {
|
|
||||||
fn user_id(&self) -> String;
|
|
||||||
fn receive(&self, resp: SyncResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum SyncResponse {
|
|
||||||
Pull(DocumentServerWSData),
|
|
||||||
Push(DocumentServerWSData),
|
|
||||||
Ack(DocumentServerWSData),
|
|
||||||
NewRevision(RepeatedRevisionPB),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct RevisionSynchronizer {
|
|
||||||
pub doc_id: String,
|
|
||||||
pub rev_id: AtomicI64,
|
|
||||||
document: Arc<RwLock<Document>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RevisionSynchronizer {
|
|
||||||
pub fn new(doc_id: &str, rev_id: i64, document: Document) -> RevisionSynchronizer {
|
|
||||||
let document = Arc::new(RwLock::new(document));
|
|
||||||
RevisionSynchronizer {
|
|
||||||
doc_id: doc_id.to_string(),
|
|
||||||
rev_id: AtomicI64::new(rev_id),
|
|
||||||
document,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(self, user, repeated_revision, persistence), err)]
|
|
||||||
pub async fn sync_revisions(
|
|
||||||
&self,
|
|
||||||
user: Arc<dyn RevisionUser>,
|
|
||||||
repeated_revision: RepeatedRevisionPB,
|
|
||||||
persistence: Arc<dyn DocumentPersistence>,
|
|
||||||
) -> Result<(), CollaborateError> {
|
|
||||||
let doc_id = self.doc_id.clone();
|
|
||||||
if repeated_revision.get_items().is_empty() {
|
|
||||||
// Return all the revisions to client
|
|
||||||
let revisions = persistence.get_doc_revisions(&doc_id).await?;
|
|
||||||
let repeated_revision = repeated_revision_from_revision_pbs(revisions)?;
|
|
||||||
let data = DocumentServerWSDataBuilder::build_push_message(&doc_id, repeated_revision);
|
|
||||||
user.receive(SyncResponse::Push(data));
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let server_base_rev_id = self.rev_id.load(SeqCst);
|
|
||||||
let first_revision = repeated_revision.get_items().first().unwrap().clone();
|
|
||||||
if self.is_applied_before(&first_revision, &persistence).await {
|
|
||||||
// Server has received this revision before, so ignore the following revisions
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
match server_base_rev_id.cmp(&first_revision.rev_id) {
|
|
||||||
Ordering::Less => {
|
|
||||||
let server_rev_id = next(server_base_rev_id);
|
|
||||||
if server_base_rev_id == first_revision.base_rev_id || server_rev_id == first_revision.rev_id {
|
|
||||||
// The rev is in the right order, just compose it.
|
|
||||||
for revision in repeated_revision.get_items() {
|
|
||||||
let _ = self.compose_revision(revision)?;
|
|
||||||
}
|
|
||||||
user.receive(SyncResponse::NewRevision(repeated_revision));
|
|
||||||
} else {
|
|
||||||
// The server document is outdated, pull the missing revision from the client.
|
|
||||||
let range = RevisionRange {
|
|
||||||
doc_id: self.doc_id.clone(),
|
|
||||||
start: server_rev_id,
|
|
||||||
end: first_revision.rev_id,
|
|
||||||
};
|
|
||||||
let msg = DocumentServerWSDataBuilder::build_pull_message(&self.doc_id, range);
|
|
||||||
user.receive(SyncResponse::Pull(msg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ordering::Equal => {
|
|
||||||
// Do nothing
|
|
||||||
log::warn!("Applied revision rev_id is the same as cur_rev_id");
|
|
||||||
}
|
|
||||||
Ordering::Greater => {
|
|
||||||
// The client document is outdated. Transform the client revision delta and then
|
|
||||||
// send the prime delta to the client. Client should compose the this prime
|
|
||||||
// delta.
|
|
||||||
let from_rev_id = first_revision.rev_id;
|
|
||||||
let to_rev_id = server_base_rev_id;
|
|
||||||
let _ = self
|
|
||||||
.push_revisions_to_user(user, persistence, from_rev_id, to_rev_id)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(self, user, persistence), fields(server_rev_id), err)]
|
|
||||||
pub async fn pong(
|
|
||||||
&self,
|
|
||||||
user: Arc<dyn RevisionUser>,
|
|
||||||
persistence: Arc<dyn DocumentPersistence>,
|
|
||||||
client_rev_id: i64,
|
|
||||||
) -> Result<(), CollaborateError> {
|
|
||||||
let doc_id = self.doc_id.clone();
|
|
||||||
let server_rev_id = self.rev_id();
|
|
||||||
tracing::Span::current().record("server_rev_id", &server_rev_id);
|
|
||||||
match server_rev_id.cmp(&client_rev_id) {
|
|
||||||
Ordering::Less => {
|
|
||||||
tracing::error!("Client should not send ping and the server should pull the revisions from the client")
|
|
||||||
}
|
|
||||||
Ordering::Equal => tracing::trace!("{} is up to date.", doc_id),
|
|
||||||
Ordering::Greater => {
|
|
||||||
// The client document is outdated. Transform the client revision delta and then
|
|
||||||
// send the prime delta to the client. Client should compose the this prime
|
|
||||||
// delta.
|
|
||||||
let from_rev_id = client_rev_id;
|
|
||||||
let to_rev_id = server_rev_id;
|
|
||||||
tracing::trace!("Push revisions to user");
|
|
||||||
let _ = self
|
|
||||||
.push_revisions_to_user(user, persistence, from_rev_id, to_rev_id)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(self, repeated_revision, persistence), fields(doc_id), err)]
|
|
||||||
pub async fn reset(
|
|
||||||
&self,
|
|
||||||
persistence: Arc<dyn DocumentPersistence>,
|
|
||||||
repeated_revision: RepeatedRevisionPB,
|
|
||||||
) -> Result<(), CollaborateError> {
|
|
||||||
let doc_id = self.doc_id.clone();
|
|
||||||
tracing::Span::current().record("doc_id", &doc_id.as_str());
|
|
||||||
let revisions: Vec<RevisionPB> = repeated_revision.get_items().to_vec();
|
|
||||||
let (_, rev_id) = pair_rev_id_from_revision_pbs(&revisions);
|
|
||||||
let delta = make_delta_from_revision_pb(revisions)?;
|
|
||||||
|
|
||||||
let _ = persistence.reset_document(&doc_id, repeated_revision).await?;
|
|
||||||
*self.document.write() = Document::from_delta(delta);
|
|
||||||
let _ = self.rev_id.fetch_update(SeqCst, SeqCst, |_e| Some(rev_id));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn doc_json(&self) -> String {
|
|
||||||
self.document.read().to_json()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compose_revision(&self, revision: &RevisionPB) -> Result<(), CollaborateError> {
|
|
||||||
let delta = RichTextDelta::from_bytes(&revision.delta_data)?;
|
|
||||||
let _ = self.compose_delta(delta)?;
|
|
||||||
let _ = self.rev_id.fetch_update(SeqCst, SeqCst, |_e| Some(revision.rev_id));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(self, revision))]
|
|
||||||
fn transform_revision(&self, revision: &RevisionPB) -> Result<(RichTextDelta, RichTextDelta), CollaborateError> {
|
|
||||||
let cli_delta = RichTextDelta::from_bytes(&revision.delta_data)?;
|
|
||||||
let result = self.document.read().delta().transform(&cli_delta)?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compose_delta(&self, delta: RichTextDelta) -> Result<(), CollaborateError> {
|
|
||||||
if delta.is_empty() {
|
|
||||||
log::warn!("Composed delta is empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.document.try_write_for(Duration::from_millis(300)) {
|
|
||||||
None => log::error!("Failed to acquire write lock of document"),
|
|
||||||
Some(mut write_guard) => {
|
|
||||||
let _ = write_guard.compose_delta(delta);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn rev_id(&self) -> i64 {
|
|
||||||
self.rev_id.load(SeqCst)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn is_applied_before(&self, new_revision: &RevisionPB, persistence: &Arc<dyn DocumentPersistence>) -> bool {
|
|
||||||
if let Ok(revisions) = persistence.get_revisions(&self.doc_id, vec![new_revision.rev_id]).await {
|
|
||||||
if let Some(revision) = revisions.first() {
|
|
||||||
if revision.md5 == new_revision.md5 {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn push_revisions_to_user(
|
|
||||||
&self,
|
|
||||||
user: Arc<dyn RevisionUser>,
|
|
||||||
persistence: Arc<dyn DocumentPersistence>,
|
|
||||||
from: i64,
|
|
||||||
to: i64,
|
|
||||||
) {
|
|
||||||
let rev_ids: Vec<i64> = (from..=to).collect();
|
|
||||||
let revisions = match persistence.get_revisions(&self.doc_id, rev_ids).await {
|
|
||||||
Ok(revisions) => {
|
|
||||||
debug_assert!(!revisions.is_empty(), "revisions should not be empty if the doc exists");
|
|
||||||
revisions
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
tracing::error!("{}", e);
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
tracing::debug!("Push revision: {} -> {} to client", from, to);
|
|
||||||
match repeated_revision_from_revision_pbs(revisions) {
|
|
||||||
Ok(repeated_revision) => {
|
|
||||||
let data = DocumentServerWSDataBuilder::build_push_message(&self.doc_id, repeated_revision);
|
|
||||||
user.receive(SyncResponse::Push(data));
|
|
||||||
}
|
|
||||||
Err(e) => tracing::error!("{}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn next(rev_id: i64) -> i64 {
|
|
||||||
rev_id + 1
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user