doc_bloc.dart 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import 'package:appflowy/plugins/document/application/document_data_pb_extension.dart';
  2. import 'package:appflowy/plugins/document/application/editor_transaction_adapter.dart';
  3. import 'package:appflowy/plugins/trash/application/trash_service.dart';
  4. import 'package:appflowy/user/application/user_service.dart';
  5. import 'package:appflowy/util/json_print.dart';
  6. import 'package:appflowy/workspace/application/view/view_listener.dart';
  7. import 'package:appflowy/workspace/application/doc/doc_listener.dart';
  8. import 'package:appflowy/plugins/document/application/doc_service.dart';
  9. import 'package:appflowy_backend/protobuf/flowy-document2/protobuf.dart';
  10. import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pbserver.dart';
  11. import 'package:appflowy_editor/appflowy_editor.dart'
  12. show EditorState, LogLevel;
  13. import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
  14. import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
  15. import 'package:flutter/foundation.dart';
  16. import 'package:flutter_bloc/flutter_bloc.dart';
  17. import 'package:freezed_annotation/freezed_annotation.dart';
  18. import 'package:dartz/dartz.dart';
  19. import 'dart:async';
  20. part 'doc_bloc.freezed.dart';
  21. class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
  22. DocumentBloc({
  23. required this.view,
  24. }) : _documentListener = DocumentListener(id: view.id),
  25. _viewListener = ViewListener(view: view),
  26. _documentService = DocumentService(),
  27. _trashService = TrashService(),
  28. super(DocumentState.initial()) {
  29. _transactionAdapter = TransactionAdapter(
  30. documentId: view.id,
  31. documentService: _documentService,
  32. );
  33. on<DocumentEvent>(_onDocumentEvent);
  34. }
  35. final ViewPB view;
  36. final DocumentListener _documentListener;
  37. final ViewListener _viewListener;
  38. final DocumentService _documentService;
  39. final TrashService _trashService;
  40. late final TransactionAdapter _transactionAdapter;
  41. EditorState? editorState;
  42. StreamSubscription? _subscription;
  43. @override
  44. Future<void> close() async {
  45. await _viewListener.stop();
  46. await _subscription?.cancel();
  47. await _documentService.closeDocument(view: view);
  48. editorState?.cancelSubscription();
  49. return super.close();
  50. }
  51. Future<void> _onDocumentEvent(
  52. DocumentEvent event,
  53. Emitter<DocumentState> emit,
  54. ) async {
  55. await event.map(
  56. initial: (Initial value) async {
  57. final state = await _fetchDocumentState();
  58. await _subscribe(state);
  59. emit(state);
  60. },
  61. deleted: (Deleted value) async {
  62. emit(state.copyWith(isDeleted: true));
  63. },
  64. restore: (Restore value) async {
  65. emit(state.copyWith(isDeleted: false));
  66. },
  67. deletePermanently: (DeletePermanently value) async {
  68. final result = await _trashService.deleteViews([view.id]);
  69. emit(state.copyWith(forceClose: result.swap().isLeft()));
  70. },
  71. restorePage: (RestorePage value) async {
  72. final result = await _trashService.putback(view.id);
  73. emit(state.copyWith(isDeleted: result.swap().isRight()));
  74. },
  75. );
  76. }
  77. Future<void> _subscribe(DocumentState state) async {
  78. _onViewChanged();
  79. _onDocumentChanged();
  80. // create the editor state
  81. await state.loadingState.whenOrNull(
  82. finish: (data) async => data.map((r) {
  83. _initAppFlowyEditorState(r);
  84. }),
  85. );
  86. }
  87. /// subscribe to the view(document page) change
  88. void _onViewChanged() {
  89. _viewListener.start(
  90. onViewDeleted: (r) =>
  91. r.swap().map((r) => add(const DocumentEvent.deleted())),
  92. onViewRestored: (r) =>
  93. r.swap().map((r) => add(const DocumentEvent.restore())),
  94. );
  95. }
  96. /// subscribe to the document content change
  97. void _onDocumentChanged() {
  98. _documentListener.start(
  99. didReceiveUpdate: (docEvent) {
  100. // todo: integrate the document change to the editor
  101. // prettyPrintJson(docEvent.toProto3Json());
  102. },
  103. );
  104. }
  105. /// Fetch document
  106. Future<DocumentState> _fetchDocumentState() async {
  107. final result = await UserBackendService.getCurrentUserProfile().then(
  108. (value) async => value.andThen(
  109. // open the document
  110. await _documentService.openDocument(view: view),
  111. ),
  112. );
  113. return state.copyWith(
  114. loadingState: DocumentLoadingState.finish(result),
  115. );
  116. }
  117. Future<void> _initAppFlowyEditorState(DocumentDataPB data) async {
  118. if (kDebugMode) {
  119. prettyPrintJson(data.toProto3Json());
  120. }
  121. final document = data.toDocument();
  122. if (document == null) {
  123. assert(false, 'document is null');
  124. return;
  125. }
  126. final editorState = EditorState(document: document);
  127. this.editorState = editorState;
  128. // subscribe to the document change from the editor
  129. _subscription = editorState.transactionStream.listen((transaction) async {
  130. await _transactionAdapter.apply(transaction, editorState);
  131. });
  132. // output the log from the editor when debug mode
  133. if (kDebugMode) {
  134. editorState.logConfiguration
  135. ..level = LogLevel.all
  136. ..handler = (log) {
  137. // Log.debug(log);
  138. };
  139. }
  140. }
  141. }
  142. @freezed
  143. class DocumentEvent with _$DocumentEvent {
  144. const factory DocumentEvent.initial() = Initial;
  145. const factory DocumentEvent.deleted() = Deleted;
  146. const factory DocumentEvent.restore() = Restore;
  147. const factory DocumentEvent.restorePage() = RestorePage;
  148. const factory DocumentEvent.deletePermanently() = DeletePermanently;
  149. }
  150. @freezed
  151. class DocumentState with _$DocumentState {
  152. const factory DocumentState({
  153. required DocumentLoadingState loadingState,
  154. required bool isDeleted,
  155. required bool forceClose,
  156. UserProfilePB? userProfilePB,
  157. }) = _DocumentState;
  158. factory DocumentState.initial() => const DocumentState(
  159. loadingState: _Loading(),
  160. isDeleted: false,
  161. forceClose: false,
  162. userProfilePB: null,
  163. );
  164. }
  165. @freezed
  166. class DocumentLoadingState with _$DocumentLoadingState {
  167. const factory DocumentLoadingState.loading() = _Loading;
  168. const factory DocumentLoadingState.finish(
  169. Either<FlowyError, DocumentDataPB> successOrFail,
  170. ) = _Finish;
  171. }