feat: support create document with initial data (#1841)

This commit is contained in:
Nathan.fooo 2023-02-10 22:24:34 +08:00 committed by GitHub
parent 1ad08ba59d
commit 8588afcda6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 49 additions and 128 deletions

View File

@ -75,8 +75,8 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
Future<void> _initial(Initial value, Emitter<DocumentState> emit) async { Future<void> _initial(Initial value, Emitter<DocumentState> emit) async {
final result = await _documentService.openDocument(view: view); final result = await _documentService.openDocument(view: view);
result.fold( result.fold(
(block) { (documentData) {
final document = Document.fromJson(jsonDecode(block.snapshot)); final document = Document.fromJson(jsonDecode(documentData.content));
editorState = EditorState(document: document); editorState = EditorState(document: document);
_listenOnDocumentChange(); _listenOnDocumentChange();
emit( emit(

View File

@ -6,14 +6,14 @@ import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart';
class DocumentService { class DocumentService {
Future<Either<DocumentSnapshotPB, FlowyError>> openDocument({ Future<Either<DocumentDataPB, FlowyError>> openDocument({
required ViewPB view, required ViewPB view,
}) async { }) async {
await FolderEventSetLatestView(ViewIdPB(value: view.id)).send(); await FolderEventSetLatestView(ViewIdPB(value: view.id)).send();
final payload = OpenDocumentContextPB() final payload = OpenDocumentPayloadPB()
..documentId = view.id ..documentId = view.id
..documentVersion = DocumentVersionPB.V1; ..version = DocumentVersionPB.V1;
// switch (view.dataFormat) { // switch (view.dataFormat) {
// case ViewDataFormatPB.DeltaFormat: // case ViewDataFormatPB.DeltaFormat:
// payload.documentVersion = DocumentVersionPB.V0; // payload.documentVersion = DocumentVersionPB.V0;

View File

@ -1,46 +0,0 @@
import 'package:app_flowy/core/grid_notification.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/notification.pb.dart';
import 'package:flowy_infra/notifier.dart';
import 'dart:async';
import 'dart:typed_data';
import 'package:dartz/dartz.dart';
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
typedef UpdateRowNotifiedValue = Either<RowPB, FlowyError>;
typedef UpdateFieldNotifiedValue = Either<List<FieldPB>, FlowyError>;
class RowListener {
final String rowId;
PublishNotifier<UpdateRowNotifiedValue>? updateRowNotifier =
PublishNotifier();
DatabaseNotificationListener? _listener;
RowListener({required this.rowId});
void start() {
_listener =
DatabaseNotificationListener(objectId: rowId, handler: _handler);
}
void _handler(DatabaseNotification ty, Either<Uint8List, FlowyError> result) {
switch (ty) {
case DatabaseNotification.DidUpdateRow:
result.fold(
(payload) =>
updateRowNotifier?.value = left(RowPB.fromBuffer(payload)),
(error) => updateRowNotifier?.value = right(error),
);
break;
default:
break;
}
}
Future<void> stop() async {
await _listener?.stop();
updateRowNotifier?.dispose();
updateRowNotifier = null;
}
}

View File

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'package:appflowy_backend/protobuf/flowy-folder/workspace.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/workspace.pb.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
@ -23,13 +24,18 @@ class AppService {
required ViewDataFormatPB dataFormatType, required ViewDataFormatPB dataFormatType,
required PluginType pluginType, required PluginType pluginType,
required ViewLayoutTypePB layoutType, required ViewLayoutTypePB layoutType,
/// The initial data should be the JSON of the doucment
/// For example: {"document":{"type":"editor","children":[]}}
String? initialData,
}) { }) {
var payload = CreateViewPayloadPB.create() final payload = CreateViewPayloadPB.create()
..belongToId = appId ..belongToId = appId
..name = name ..name = name
..desc = desc ?? "" ..desc = desc ?? ""
..dataFormat = dataFormatType ..dataFormat = dataFormatType
..layout = layoutType; ..layout = layoutType
..initialData = utf8.encode(initialData ?? "");
return FolderEventCreateView(payload).send(); return FolderEventCreateView(payload).send();
} }
@ -118,54 +124,6 @@ class AppService {
} }
} }
extension AppFlowy on Either {
T? getLeftOrNull<T>() {
if (isLeft()) {
final result = fold<T?>((l) => l, (r) => null);
return result;
}
return null;
}
Future<List<Tuple2<AppPB, List<ViewPB>>>> fetchViews(
ViewLayoutTypePB layoutType) async {
final result = <Tuple2<AppPB, List<ViewPB>>>[];
return FolderEventReadCurrentWorkspace().send().then((value) async {
final workspaces = value.getLeftOrNull<WorkspaceSettingPB>();
if (workspaces != null) {
final apps = workspaces.workspace.apps.items;
for (var app in apps) {
final views = await getViews(appId: app.id).then(
(value) => value
.getLeftOrNull<List<ViewPB>>()
?.where((e) => e.layout == layoutType)
.toList(),
);
if (views != null && views.isNotEmpty) {
result.add(Tuple2(app, views));
}
}
}
return result;
});
}
Future<Either<ViewPB, FlowyError>> getView(
String appID,
String viewID,
) async {
final payload = AppIdPB.create()..value = appID;
return FolderEventReadApp(payload).send().then((result) {
return result.fold(
(app) => left(
app.belongings.items.firstWhere((e) => e.id == viewID),
),
(error) => right(error),
);
});
}
}
extension AppFlowy on Either { extension AppFlowy on Either {
T? getLeftOrNull<T>() { T? getLeftOrNull<T>() {
if (isLeft()) { if (isLeft()) {

View File

@ -5,6 +5,7 @@ use flowy_client_ws::FlowyWebSocketConnect;
use flowy_database::entities::LayoutTypePB; use flowy_database::entities::LayoutTypePB;
use flowy_database::manager::{make_database_view_data, DatabaseManager}; use flowy_database::manager::{make_database_view_data, DatabaseManager};
use flowy_database::util::{make_default_board, make_default_calendar, make_default_grid}; use flowy_database::util::{make_default_board, make_default_calendar, make_default_grid};
use flowy_document::editor::make_transaction_from_document_content;
use flowy_document::DocumentManager; use flowy_document::DocumentManager;
use flowy_folder::entities::{ViewDataFormatPB, ViewLayoutTypePB, ViewPB}; use flowy_folder::entities::{ViewDataFormatPB, ViewLayoutTypePB, ViewPB};
use flowy_folder::manager::{ViewDataProcessor, ViewDataProcessorMap}; use flowy_folder::manager::{ViewDataProcessor, ViewDataProcessorMap};
@ -150,7 +151,15 @@ impl ViewDataProcessor for DocumentViewDataProcessor {
) -> FutureResult<(), FlowyError> { ) -> FutureResult<(), FlowyError> {
// Only accept Document type // Only accept Document type
debug_assert_eq!(layout, ViewLayoutTypePB::Document); debug_assert_eq!(layout, ViewLayoutTypePB::Document);
let revision = Revision::initial_revision(view_id, view_data); let view_data = match String::from_utf8(view_data.to_vec()) {
Ok(content) => match make_transaction_from_document_content(&content) {
Ok(transaction) => transaction.to_bytes().unwrap_or(vec![]),
Err(_) => vec![],
},
Err(_) => vec![],
};
let revision = Revision::initial_revision(view_id, Bytes::from(view_data));
let view_id = view_id.to_string(); let view_id = view_id.to_string();
let manager = self.0.clone(); let manager = self.0.clone();
@ -199,7 +208,7 @@ impl ViewDataProcessor for DocumentViewDataProcessor {
}) })
} }
fn create_view_from_delta_data( fn create_view_with_data(
&self, &self,
_user_id: &str, _user_id: &str,
_view_id: &str, _view_id: &str,
@ -279,7 +288,7 @@ impl ViewDataProcessor for GridViewDataProcessor {
}) })
} }
fn create_view_from_delta_data( fn create_view_with_data(
&self, &self,
user_id: &str, user_id: &str,
view_id: &str, view_id: &str,

View File

@ -58,13 +58,13 @@ impl TryInto<EditParams> for EditPayloadPB {
} }
#[derive(Default, ProtoBuf)] #[derive(Default, ProtoBuf)]
pub struct DocumentSnapshotPB { pub struct DocumentDataPB {
#[pb(index = 1)] #[pb(index = 1)]
pub doc_id: String, pub doc_id: String,
/// Encode in JSON format /// Encode in JSON format
#[pb(index = 2)] #[pb(index = 2)]
pub snapshot: String, pub content: String,
} }
#[derive(Default, ProtoBuf)] #[derive(Default, ProtoBuf)]
@ -96,12 +96,12 @@ impl std::default::Default for DocumentVersionPB {
} }
#[derive(Default, ProtoBuf)] #[derive(Default, ProtoBuf)]
pub struct OpenDocumentContextPB { pub struct OpenDocumentPayloadPB {
#[pb(index = 1)] #[pb(index = 1)]
pub document_id: String, pub document_id: String,
#[pb(index = 2)] #[pb(index = 2)]
pub document_version: DocumentVersionPB, pub version: DocumentVersionPB,
} }
#[derive(Default, Debug)] #[derive(Default, Debug)]

View File

@ -1,5 +1,5 @@
use crate::entities::{ use crate::entities::{
DocumentSnapshotPB, EditParams, EditPayloadPB, ExportDataPB, ExportParams, ExportPayloadPB, OpenDocumentContextPB, DocumentDataPB, EditParams, EditPayloadPB, ExportDataPB, ExportParams, ExportPayloadPB, OpenDocumentPayloadPB,
}; };
use crate::DocumentManager; use crate::DocumentManager;
use flowy_error::FlowyError; use flowy_error::FlowyError;
@ -9,15 +9,15 @@ use std::convert::TryInto;
use std::sync::Arc; use std::sync::Arc;
pub(crate) async fn get_document_handler( pub(crate) async fn get_document_handler(
data: AFPluginData<OpenDocumentContextPB>, data: AFPluginData<OpenDocumentPayloadPB>,
manager: AFPluginState<Arc<DocumentManager>>, manager: AFPluginState<Arc<DocumentManager>>,
) -> DataResult<DocumentSnapshotPB, FlowyError> { ) -> DataResult<DocumentDataPB, FlowyError> {
let context: OpenDocumentContextPB = data.into_inner(); let context: OpenDocumentPayloadPB = data.into_inner();
let editor = manager.open_document_editor(&context.document_id).await?; let editor = manager.open_document_editor(&context.document_id).await?;
let document_data = editor.export().await?; let document_data = editor.export().await?;
data_result(DocumentSnapshotPB { data_result(DocumentDataPB {
doc_id: context.document_id, doc_id: context.document_id,
snapshot: document_data, content: document_data,
}) })
} }

View File

@ -19,7 +19,7 @@ pub fn init(document_manager: Arc<DocumentManager>) -> AFPlugin {
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)] #[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
#[event_err = "FlowyError"] #[event_err = "FlowyError"]
pub enum DocumentEvent { pub enum DocumentEvent {
#[event(input = "OpenDocumentContextPB", output = "DocumentSnapshotPB")] #[event(input = "OpenDocumentPayloadPB", output = "DocumentDataPB")]
GetDocument = 0, GetDocument = 0,
#[event(input = "EditPayloadPB")] #[event(input = "EditPayloadPB")]

View File

@ -162,7 +162,7 @@ pub struct CreateViewPayloadPB {
pub layout: ViewLayoutTypePB, pub layout: ViewLayoutTypePB,
#[pb(index = 7)] #[pb(index = 7)]
pub view_content_data: Vec<u8>, pub initial_data: Vec<u8>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -174,7 +174,7 @@ pub struct CreateViewParams {
pub data_format: ViewDataFormatPB, pub data_format: ViewDataFormatPB,
pub layout: ViewLayoutTypePB, pub layout: ViewLayoutTypePB,
pub view_id: String, pub view_id: String,
pub view_content_data: Vec<u8>, pub initial_data: Vec<u8>,
} }
impl TryInto<CreateViewParams> for CreateViewPayloadPB { impl TryInto<CreateViewParams> for CreateViewPayloadPB {
@ -197,7 +197,7 @@ impl TryInto<CreateViewParams> for CreateViewPayloadPB {
layout: self.layout, layout: self.layout,
thumbnail, thumbnail,
view_id, view_id,
view_content_data: self.view_content_data, initial_data: self.initial_data,
}) })
} }
} }

View File

@ -282,7 +282,7 @@ pub trait ViewDataProcessor {
data_format: ViewDataFormatPB, data_format: ViewDataFormatPB,
) -> FutureResult<Bytes, FlowyError>; ) -> FutureResult<Bytes, FlowyError>;
fn create_view_from_delta_data( fn create_view_with_data(
&self, &self,
user_id: &str, user_id: &str,
view_id: &str, view_id: &str,

View File

@ -59,7 +59,7 @@ impl ViewController {
) -> Result<ViewRevision, FlowyError> { ) -> Result<ViewRevision, FlowyError> {
let processor = self.get_data_processor(params.data_format.clone())?; let processor = self.get_data_processor(params.data_format.clone())?;
let user_id = self.user.user_id()?; let user_id = self.user.user_id()?;
if params.view_content_data.is_empty() { if params.initial_data.is_empty() {
tracing::trace!("Create view with build-in data"); tracing::trace!("Create view with build-in data");
let view_data = processor let view_data = processor
.create_default_view( .create_default_view(
@ -69,14 +69,14 @@ impl ViewController {
params.data_format.clone(), params.data_format.clone(),
) )
.await?; .await?;
params.view_content_data = view_data.to_vec(); params.initial_data = view_data.to_vec();
} else { } else {
tracing::trace!("Create view with view data"); tracing::trace!("Create view with view data");
let delta_data = processor let view_data = processor
.create_view_from_delta_data( .create_view_with_data(
&user_id, &user_id,
&params.view_id, &params.view_id,
params.view_content_data.clone(), params.initial_data.clone(),
params.layout.clone(), params.layout.clone(),
) )
.await?; .await?;
@ -84,7 +84,7 @@ impl ViewController {
&params.view_id, &params.view_id,
params.data_format.clone(), params.data_format.clone(),
params.layout.clone(), params.layout.clone(),
delta_data, view_data,
) )
.await?; .await?;
}; };
@ -232,7 +232,7 @@ impl ViewController {
thumbnail: view_rev.thumbnail, thumbnail: view_rev.thumbnail,
data_format: view_rev.data_format.into(), data_format: view_rev.data_format.into(),
layout: view_rev.layout.into(), layout: view_rev.layout.into(),
view_content_data: view_data.to_vec(), initial_data: view_data.to_vec(),
view_id: gen_view_id(), view_id: gen_view_id(),
}; };

View File

@ -363,7 +363,7 @@ pub async fn create_view(
thumbnail: None, thumbnail: None,
data_format: data_type, data_format: data_type,
layout, layout,
view_content_data: vec![], initial_data: vec![],
}; };
FolderEventBuilder::new(sdk.clone()) FolderEventBuilder::new(sdk.clone())
.event(CreateView) .event(CreateView)

View File

@ -119,7 +119,7 @@ async fn create_view(
thumbnail: Some("http://1.png".to_string()), thumbnail: Some("http://1.png".to_string()),
data_format, data_format,
layout, layout,
view_content_data: data, initial_data: data,
}; };
FolderEventBuilder::new(sdk.clone()) FolderEventBuilder::new(sdk.clone())