[flutter]: create read me when user sign up

This commit is contained in:
appflowy 2021-11-13 11:53:50 +08:00
parent b4237b1986
commit 1896c79379
17 changed files with 56 additions and 29 deletions
app_flowy
lib/workspace
packages/flowy_sdk/lib/dispatch
backend
src/service/user
tests/document
rust-lib
flowy-document-infra/src
flowy-document/src
flowy-sdk/src/deps_resolve
flowy-workspace-infra/src/entities/view
flowy-workspace/src/services

@ -1,6 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-document/doc.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-document-infra/doc.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
abstract class IDoc { abstract class IDoc {

@ -4,7 +4,7 @@ import 'dart:typed_data';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:app_flowy/workspace/domain/i_doc.dart'; import 'package:app_flowy/workspace/domain/i_doc.dart';
import 'package:app_flowy/workspace/infrastructure/repos/doc_repo.dart'; import 'package:app_flowy/workspace/infrastructure/repos/doc_repo.dart';
import 'package:flowy_sdk/protobuf/flowy-document/doc.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-document-infra/doc.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
class IDocImpl extends IDoc { class IDocImpl extends IDoc {

@ -1,6 +1,6 @@
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart'; import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-document/doc.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-document-infra/doc.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace-infra/view_query.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-workspace-infra/view_query.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';

@ -15,7 +15,8 @@ import 'package:flowy_sdk/ffi.dart' as ffi;
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart'; import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart';
import 'package:flowy_sdk/protobuf/dart-ffi/protobuf.dart'; import 'package:flowy_sdk/protobuf/dart-ffi/protobuf.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace-infra/protobuf.dart'; import 'package:flowy_sdk/protobuf/flowy-workspace-infra/protobuf.dart';
import 'package:flowy_sdk/protobuf/flowy-document/protobuf.dart'; import 'package:flowy_sdk/protobuf/flowy-document-infra/protobuf.dart';
// ignore: unused_import // ignore: unused_import
import 'package:flowy_sdk/protobuf/flowy-infra/protobuf.dart'; import 'package:flowy_sdk/protobuf/flowy-infra/protobuf.dart';
import 'package:protobuf/protobuf.dart'; import 'package:protobuf/protobuf.dart';

@ -8,7 +8,7 @@ use crate::{
use crate::service::view::{create_view_with_args, sql_builder::NewViewSqlBuilder}; use crate::service::view::{create_view_with_args, sql_builder::NewViewSqlBuilder};
use chrono::Utc; use chrono::Utc;
use flowy_document_infra::document_default::doc_initial_string; use flowy_document_infra::user_default::doc_initial_string;
use flowy_net::errors::ServerError; use flowy_net::errors::ServerError;
use flowy_workspace_infra::protobuf::Workspace; use flowy_workspace_infra::protobuf::Workspace;
use std::convert::TryInto; use std::convert::TryInto;

@ -73,10 +73,9 @@ impl ScriptContext {
async fn open_doc(&mut self) { async fn open_doc(&mut self) {
let flowy_document = self.flowy_test.sdk.flowy_document.clone(); let flowy_document = self.flowy_test.sdk.flowy_document.clone();
let pool = self.client_user_session.db_pool().unwrap();
let doc_id = self.doc_id.clone(); let doc_id = self.doc_id.clone();
let edit_context = flowy_document.open(DocIdentifier { doc_id }, pool).await.unwrap(); let edit_context = flowy_document.open(DocIdentifier { doc_id }).await.unwrap();
self.client_edit_context = Some(edit_context); self.client_edit_context = Some(edit_context);
} }

@ -1,4 +1,4 @@
pub mod document_default;
pub mod entities; pub mod entities;
pub mod protobuf; pub mod protobuf;
pub mod user_default;
pub mod util; pub mod util;

@ -1,23 +1,25 @@
use flowy_ot::core::{Delta, DeltaBuilder}; use flowy_ot::core::{Delta, DeltaBuilder};
#[allow(dead_code)]
#[inline] #[inline]
pub fn doc_initial_delta() -> Delta { DeltaBuilder::new().insert("\n").build() } pub fn doc_initial_delta() -> Delta { DeltaBuilder::new().insert("\n").build() }
#[allow(dead_code)]
#[inline] #[inline]
pub fn doc_initial_string() -> String { doc_initial_delta().to_json() } pub fn doc_initial_string() -> String { doc_initial_delta().to_json() }
#[allow(dead_code)]
#[inline] #[inline]
pub fn doc_initial_bytes() -> Vec<u8> { doc_initial_string().into_bytes() } pub fn initial_read_me() -> Delta {
let json = include_str!("READ_ME.json");
let delta = Delta::from_json(json).unwrap();
delta
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::user_default::initial_read_me;
use flowy_ot::core::Delta; use flowy_ot::core::Delta;
#[test] #[test]
fn load_read_me() { fn load_read_me() {
let json = include_str!("READ_ME.json"); println!("{}", initial_read_me().to_json());
let delta = Delta::from_json(json).unwrap();
assert_eq!(delta.to_json(), json);
} }
} }

@ -15,10 +15,12 @@ pub trait DocumentUser: Send + Sync {
fn user_dir(&self) -> Result<String, DocError>; fn user_dir(&self) -> Result<String, DocError>;
fn user_id(&self) -> Result<String, DocError>; fn user_id(&self) -> Result<String, DocError>;
fn token(&self) -> Result<String, DocError>; fn token(&self) -> Result<String, DocError>;
fn db_pool(&self) -> Result<Arc<ConnectionPool>, DocError>;
} }
pub struct FlowyDocument { pub struct FlowyDocument {
doc_ctrl: Arc<DocController>, doc_ctrl: Arc<DocController>,
user: Arc<dyn DocumentUser>,
} }
impl FlowyDocument { impl FlowyDocument {
@ -28,8 +30,8 @@ impl FlowyDocument {
server_config: &ServerConfig, server_config: &ServerConfig,
) -> FlowyDocument { ) -> FlowyDocument {
let server = construct_doc_server(server_config); let server = construct_doc_server(server_config);
let controller = Arc::new(DocController::new(server.clone(), user.clone(), ws_manager.clone())); let doc_ctrl = Arc::new(DocController::new(server.clone(), user.clone(), ws_manager.clone()));
Self { doc_ctrl: controller } Self { doc_ctrl, user }
} }
pub fn init(&self) -> Result<(), DocError> { pub fn init(&self) -> Result<(), DocError> {
@ -42,8 +44,8 @@ impl FlowyDocument {
Ok(()) Ok(())
} }
pub async fn open(&self, params: DocIdentifier, pool: Arc<ConnectionPool>) -> Result<Arc<ClientEditDoc>, DocError> { pub async fn open(&self, params: DocIdentifier) -> Result<Arc<ClientEditDoc>, DocError> {
let edit_context = self.doc_ctrl.open(params, pool).await?; let edit_context = self.doc_ctrl.open(params, self.user.db_pool()?).await?;
Ok(edit_context) Ok(edit_context)
} }
@ -65,7 +67,10 @@ impl FlowyDocument {
pub async fn apply_doc_delta(&self, params: DocDelta) -> Result<DocDelta, DocError> { pub async fn apply_doc_delta(&self, params: DocDelta) -> Result<DocDelta, DocError> {
// workaround: compare the rust's delta with flutter's delta. Will be removed // workaround: compare the rust's delta with flutter's delta. Will be removed
// very soon // very soon
let doc = self.doc_ctrl.apply_local_delta(params.clone()).await?; let doc = self
.doc_ctrl
.apply_local_delta(params.clone(), self.user.db_pool()?)
.await?;
Ok(doc) Ok(doc)
} }
} }

@ -30,10 +30,10 @@ impl DocCache {
self.inner.insert(doc_id, doc); self.inner.insert(doc_id, doc);
} }
pub(crate) fn is_opened(&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) -> Result<Arc<ClientEditDoc>, DocError> { pub(crate) fn get(&self, doc_id: &str) -> Result<Arc<ClientEditDoc>, DocError> {
if !self.is_opened(&doc_id) { if !self.contains(&doc_id) {
return Err(doc_not_found()); return Err(doc_not_found());
} }
let opened_doc = self.inner.get(doc_id).unwrap(); let opened_doc = self.inner.get(doc_id).unwrap();

@ -47,7 +47,7 @@ impl DocController {
params: DocIdentifier, params: DocIdentifier,
pool: Arc<ConnectionPool>, pool: Arc<ConnectionPool>,
) -> Result<Arc<ClientEditDoc>, DocError> { ) -> Result<Arc<ClientEditDoc>, DocError> {
if self.cache.is_opened(&params.doc_id) == false { if self.cache.contains(&params.doc_id) == false {
let edit_ctx = self.make_edit_context(&params.doc_id, pool.clone()).await?; let edit_ctx = self.make_edit_context(&params.doc_id, pool.clone()).await?;
return Ok(edit_ctx); return Ok(edit_ctx);
} }
@ -74,8 +74,17 @@ impl DocController {
// as None e.g. // as None e.g.
// json : {"retain":7,"attributes":{"bold":null}} // json : {"retain":7,"attributes":{"bold":null}}
// deserialize delta: [ {retain: 7, attributes: {Bold: AttributeValue(None)}} ] // deserialize delta: [ {retain: 7, attributes: {Bold: AttributeValue(None)}} ]
#[tracing::instrument(level = "debug", skip(self, delta), fields(doc_id = %delta.doc_id), err)] #[tracing::instrument(level = "debug", skip(self, delta, db_pool), fields(doc_id = %delta.doc_id), err)]
pub(crate) async fn apply_local_delta(&self, delta: DocDelta) -> Result<DocDelta, DocError> { pub(crate) async fn apply_local_delta(
&self,
delta: DocDelta,
db_pool: Arc<ConnectionPool>,
) -> Result<DocDelta, DocError> {
if !self.cache.contains(&delta.doc_id) {
let doc_identifier: DocIdentifier = delta.doc_id.clone().into();
let _ = self.open(doc_identifier, db_pool).await?;
}
let edit_doc_ctx = self.cache.get(&delta.doc_id)?; let edit_doc_ctx = self.cache.get(&delta.doc_id)?;
let _ = edit_doc_ctx.composing_local_delta(Bytes::from(delta.data)).await?; let _ = edit_doc_ctx.composing_local_delta(Bytes::from(delta.data)).await?;
Ok(edit_doc_ctx.delta().await?) Ok(edit_doc_ctx.delta().await?)

@ -2,7 +2,7 @@ use crate::{
errors::DocError, errors::DocError,
services::doc::{view::View, History, UndoResult, RECORD_THRESHOLD}, services::doc::{view::View, History, UndoResult, RECORD_THRESHOLD},
}; };
use flowy_document_infra::document_default::doc_initial_delta; use flowy_document_infra::user_default::doc_initial_delta;
use flowy_ot::core::*; use flowy_ot::core::*;
use tokio::sync::mpsc; use tokio::sync::mpsc;

@ -1,7 +1,7 @@
use crate::{errors::DocError, services::server::DocumentServerAPI}; use crate::{errors::DocError, services::server::DocumentServerAPI};
use flowy_document_infra::{ use flowy_document_infra::{
document_default::doc_initial_string,
entities::doc::{CreateDocParams, Doc, DocIdentifier, UpdateDocParams}, entities::doc::{CreateDocParams, Doc, DocIdentifier, UpdateDocParams},
user_default::doc_initial_string,
}; };
use flowy_infra::future::ResultFuture; use flowy_infra::future::ResultFuture;

@ -8,6 +8,7 @@ use flowy_document::{
use flowy_user::{errors::ErrorCode, services::user::UserSession}; use flowy_user::{errors::ErrorCode, services::user::UserSession};
use flowy_ws::{WsMessage, WsMessageHandler, WsModule}; use flowy_ws::{WsMessage, WsMessageHandler, WsModule};
use flowy_database::ConnectionPool;
use flowy_document::services::ws::{DocumentWebSocket, WsDocumentManager}; use flowy_document::services::ws::{DocumentWebSocket, WsDocumentManager};
use flowy_user::errors::UserError; use flowy_user::errors::UserError;
use std::{path::Path, sync::Arc}; use std::{path::Path, sync::Arc};
@ -63,6 +64,8 @@ impl DocumentUser for DocumentUserImpl {
fn user_id(&self) -> Result<String, DocError> { self.user.user_id().map_err(map_user_error) } fn user_id(&self) -> Result<String, DocError> { self.user.user_id().map_err(map_user_error) }
fn token(&self) -> Result<String, DocError> { self.user.token().map_err(map_user_error) } fn token(&self) -> Result<String, DocError> { self.user.token().map_err(map_user_error) }
fn db_pool(&self) -> Result<Arc<ConnectionPool>, DocError> { self.user.db_pool().map_err(map_user_error) }
} }
struct WsSenderImpl { struct WsSenderImpl {

@ -8,7 +8,7 @@ use crate::{
}, },
}; };
use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_document_infra::document_default::doc_initial_string; use flowy_document_infra::user_default::doc_initial_string;
use std::convert::TryInto; use std::convert::TryInto;
#[derive(PartialEq, Debug, ProtoBuf_Enum, Clone)] #[derive(PartialEq, Debug, ProtoBuf_Enum, Clone)]

@ -108,7 +108,7 @@ impl ViewController {
#[tracing::instrument(level = "debug", skip(self, params), fields(doc_id = %params.doc_id), err)] #[tracing::instrument(level = "debug", skip(self, params), fields(doc_id = %params.doc_id), err)]
pub(crate) async fn open_view(&self, params: DocIdentifier) -> Result<DocDelta, WorkspaceError> { pub(crate) async fn open_view(&self, params: DocIdentifier) -> Result<DocDelta, WorkspaceError> {
let doc_id = params.doc_id.clone(); let doc_id = params.doc_id.clone();
let edit_context = self.document.open(params, self.database.db_pool()?).await?; let edit_context = self.document.open(params).await?;
KV::set_str(LATEST_VIEW_ID, doc_id); KV::set_str(LATEST_VIEW_ID, doc_id);
Ok(edit_context.delta().await.map_err(internal_error)?) Ok(edit_context.delta().await.map_err(internal_error)?)

@ -7,6 +7,7 @@ use crate::{
}; };
use chrono::Utc; use chrono::Utc;
use flowy_database::SqliteConnection; use flowy_database::SqliteConnection;
use flowy_document_infra::{entities::doc::DocDelta, user_default::initial_read_me};
use flowy_infra::kv::KV; use flowy_infra::kv::KV;
use flowy_workspace_infra::{ use flowy_workspace_infra::{
entities::{app::RepeatedApp, view::View, workspace::*}, entities::{app::RepeatedApp, view::View, workspace::*},
@ -101,6 +102,13 @@ impl WorkspaceController {
let _ = self.app_controller.create_app(app).await?; let _ = self.app_controller.create_app(app).await?;
for (index, view) in views.into_iter().enumerate() { for (index, view) in views.into_iter().enumerate() {
if index == 0 { if index == 0 {
let delta = initial_read_me();
let doc_delta = DocDelta {
doc_id: view.id.clone(),
data: delta.to_json(),
};
let _ = self.view_controller.apply_doc_delta(doc_delta).await?;
self.view_controller.set_latest_view(&view); self.view_controller.set_latest_view(&view);
} }
let _ = self.view_controller.create_view(view).await?; let _ = self.view_controller.create_view(view).await?;