mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-07-26 02:21:39 +00:00

* feat: copy link to block * chore: add comment * feat: using mention bloc to manage the mention bloc status * feat: use mention page bloc to manage mentioned page status * feat: observe mention block content changes * feat: sync the block content * fix: integration test * fix: mentioned block display name contains unnessary - * fix: handle block was deleted case * chore: move the get doc block and get mentioned page status to service * feat: support copy link to page * test: add copy link to block tests * test: add copy link to block(same pge) tests * test: open the mentioned page * fix: integration test * Revert "fix: integration test" This reverts commit f4466b22d8a40262fa992f9e0dff1bf055005f16. * fix: integration test
223 lines
6.8 KiB
Dart
223 lines
6.8 KiB
Dart
import 'package:appflowy/plugins/document/application/document_data_pb_extension.dart';
|
|
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
|
import 'package:appflowy_backend/log.dart';
|
|
import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart';
|
|
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
|
import 'package:appflowy_result/appflowy_result.dart';
|
|
import 'package:fixnum/fixnum.dart';
|
|
|
|
class DocumentService {
|
|
// unused now.
|
|
Future<FlowyResult<void, FlowyError>> createDocument({
|
|
required ViewPB view,
|
|
}) async {
|
|
final canOpen = await openDocument(documentId: view.id);
|
|
if (canOpen.isSuccess) {
|
|
return FlowyResult.success(null);
|
|
}
|
|
final payload = CreateDocumentPayloadPB()..documentId = view.id;
|
|
final result = await DocumentEventCreateDocument(payload).send();
|
|
return result;
|
|
}
|
|
|
|
Future<FlowyResult<DocumentDataPB, FlowyError>> openDocument({
|
|
required String documentId,
|
|
}) async {
|
|
final payload = OpenDocumentPayloadPB()..documentId = documentId;
|
|
final result = await DocumentEventOpenDocument(payload).send();
|
|
return result;
|
|
}
|
|
|
|
Future<FlowyResult<DocumentDataPB, FlowyError>> getDocument({
|
|
required String documentId,
|
|
}) async {
|
|
final payload = OpenDocumentPayloadPB()..documentId = documentId;
|
|
final result = await DocumentEventGetDocumentData(payload).send();
|
|
return result;
|
|
}
|
|
|
|
Future<FlowyResult<(DocumentDataPB, BlockPB, Node), FlowyError>>
|
|
getDocumentNode({
|
|
required String documentId,
|
|
required String blockId,
|
|
}) async {
|
|
final documentResult = await getDocument(documentId: documentId);
|
|
final document = documentResult.fold((l) => l, (f) => null);
|
|
if (document == null) {
|
|
Log.error('unable to get the document for page $documentId');
|
|
return FlowyResult.failure(FlowyError(msg: 'Document not found'));
|
|
}
|
|
|
|
final blockResult = await getBlockFromDocument(
|
|
document: document,
|
|
blockId: blockId,
|
|
);
|
|
final block = blockResult.fold((l) => l, (f) => null);
|
|
if (block == null) {
|
|
Log.error(
|
|
'unable to get the block $blockId from the document $documentId',
|
|
);
|
|
return FlowyResult.failure(FlowyError(msg: 'Block not found'));
|
|
}
|
|
|
|
final node = document.buildNode(blockId);
|
|
if (node == null) {
|
|
Log.error(
|
|
'unable to get the node for block $blockId in document $documentId',
|
|
);
|
|
return FlowyResult.failure(FlowyError(msg: 'Node not found'));
|
|
}
|
|
|
|
return FlowyResult.success((document, block, node));
|
|
}
|
|
|
|
Future<FlowyResult<BlockPB, FlowyError>> getBlockFromDocument({
|
|
required DocumentDataPB document,
|
|
required String blockId,
|
|
}) async {
|
|
final block = document.blocks[blockId];
|
|
|
|
if (block != null) {
|
|
return FlowyResult.success(block);
|
|
}
|
|
|
|
return FlowyResult.failure(
|
|
FlowyError(
|
|
msg: 'Block($blockId) not found in Document(${document.pageId})',
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<FlowyResult<void, FlowyError>> closeDocument({
|
|
required String viewId,
|
|
}) async {
|
|
final payload = ViewIdPB()..value = viewId;
|
|
final result = await FolderEventCloseView(payload).send();
|
|
return result;
|
|
}
|
|
|
|
Future<FlowyResult<void, FlowyError>> applyAction({
|
|
required String documentId,
|
|
required Iterable<BlockActionPB> actions,
|
|
}) async {
|
|
final payload = ApplyActionPayloadPB(
|
|
documentId: documentId,
|
|
actions: actions,
|
|
);
|
|
final result = await DocumentEventApplyAction(payload).send();
|
|
return result;
|
|
}
|
|
|
|
/// Creates a new external text.
|
|
///
|
|
/// Normally, it's used to the block that needs sync long text.
|
|
///
|
|
/// the delta parameter is the json representation of the delta.
|
|
Future<FlowyResult<void, FlowyError>> createExternalText({
|
|
required String documentId,
|
|
required String textId,
|
|
String? delta,
|
|
}) async {
|
|
final payload = TextDeltaPayloadPB(
|
|
documentId: documentId,
|
|
textId: textId,
|
|
delta: delta,
|
|
);
|
|
final result = await DocumentEventCreateText(payload).send();
|
|
return result;
|
|
}
|
|
|
|
/// Updates the external text.
|
|
///
|
|
/// this function is compatible with the [createExternalText] function.
|
|
///
|
|
/// the delta parameter is the json representation of the delta too.
|
|
Future<FlowyResult<void, FlowyError>> updateExternalText({
|
|
required String documentId,
|
|
required String textId,
|
|
String? delta,
|
|
}) async {
|
|
final payload = TextDeltaPayloadPB(
|
|
documentId: documentId,
|
|
textId: textId,
|
|
delta: delta,
|
|
);
|
|
final result = await DocumentEventApplyTextDeltaEvent(payload).send();
|
|
return result;
|
|
}
|
|
|
|
/// Upload a file to the cloud storage.
|
|
Future<FlowyResult<UploadedFilePB, FlowyError>> uploadFile({
|
|
required String localFilePath,
|
|
required String documentId,
|
|
}) async {
|
|
final workspace = await FolderEventReadCurrentWorkspace().send();
|
|
return workspace.fold(
|
|
(l) async {
|
|
final payload = UploadFileParamsPB(
|
|
workspaceId: l.id,
|
|
localFilePath: localFilePath,
|
|
documentId: documentId,
|
|
);
|
|
return DocumentEventUploadFile(payload).send();
|
|
},
|
|
(r) async {
|
|
return FlowyResult.failure(FlowyError(msg: 'Workspace not found'));
|
|
},
|
|
);
|
|
}
|
|
|
|
/// Download a file from the cloud storage.
|
|
Future<FlowyResult<void, FlowyError>> downloadFile({
|
|
required String url,
|
|
}) async {
|
|
final workspace = await FolderEventReadCurrentWorkspace().send();
|
|
return workspace.fold((l) async {
|
|
final payload = DownloadFilePB(
|
|
url: url,
|
|
);
|
|
final result = await DocumentEventDownloadFile(payload).send();
|
|
return result;
|
|
}, (r) async {
|
|
return FlowyResult.failure(FlowyError(msg: 'Workspace not found'));
|
|
});
|
|
}
|
|
|
|
/// Sync the awareness states
|
|
/// For example, the cursor position, selection, who is viewing the document.
|
|
Future<FlowyResult<void, FlowyError>> syncAwarenessStates({
|
|
required String documentId,
|
|
Selection? selection,
|
|
String? metadata,
|
|
}) async {
|
|
final payload = UpdateDocumentAwarenessStatePB(
|
|
documentId: documentId,
|
|
selection: convertSelectionToAwarenessSelection(selection),
|
|
metadata: metadata,
|
|
);
|
|
|
|
final result = await DocumentEventSetAwarenessState(payload).send();
|
|
return result;
|
|
}
|
|
|
|
DocumentAwarenessSelectionPB? convertSelectionToAwarenessSelection(
|
|
Selection? selection,
|
|
) {
|
|
if (selection == null) {
|
|
return null;
|
|
}
|
|
return DocumentAwarenessSelectionPB(
|
|
start: DocumentAwarenessPositionPB(
|
|
offset: Int64(selection.startIndex),
|
|
path: selection.start.path.map((e) => Int64(e)),
|
|
),
|
|
end: DocumentAwarenessPositionPB(
|
|
offset: Int64(selection.endIndex),
|
|
path: selection.end.path.map((e) => Int64(e)),
|
|
),
|
|
);
|
|
}
|
|
}
|