Lucas 37a2fb17b5
feat: copy link to block (#6523)
* 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
2024-10-14 14:04:42 +08:00

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)),
),
);
}
}