mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: support create document with initial data (#1841)
This commit is contained in:
parent
1ad08ba59d
commit
8588afcda6
@ -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(
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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()) {
|
||||||
|
@ -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,
|
||||||
|
@ -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)]
|
||||||
|
@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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")]
|
||||||
|
@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
¶ms.view_id,
|
¶ms.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 {
|
|||||||
¶ms.view_id,
|
¶ms.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(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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())
|
||||||
|
Loading…
Reference in New Issue
Block a user