doc_bloc.dart 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import 'dart:convert';
  2. import 'package:app_flowy/workspace/application/doc/doc_service.dart';
  3. import 'package:app_flowy/workspace/application/trash/trash_service.dart';
  4. import 'package:app_flowy/workspace/application/view/view_listener.dart';
  5. import 'package:flowy_sdk/protobuf/flowy-folder-data-model/trash.pb.dart';
  6. import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
  7. import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
  8. import 'package:flutter_quill/flutter_quill.dart' show Document, Delta;
  9. import 'package:flowy_sdk/log.dart';
  10. import 'package:flutter_bloc/flutter_bloc.dart';
  11. import 'package:freezed_annotation/freezed_annotation.dart';
  12. import 'package:dartz/dartz.dart';
  13. import 'dart:async';
  14. part 'doc_bloc.freezed.dart';
  15. typedef FlutterQuillDocument = Document;
  16. class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
  17. final View view;
  18. final DocumentService service;
  19. final ViewListener listener;
  20. final TrashService trashService;
  21. late FlutterQuillDocument document;
  22. StreamSubscription? _subscription;
  23. DocumentBloc({
  24. required this.view,
  25. required this.service,
  26. required this.listener,
  27. required this.trashService,
  28. }) : super(DocumentState.initial()) {
  29. on<DocumentEvent>((event, emit) async {
  30. await event.map(
  31. initial: (Initial value) async {
  32. await _initial(value, emit);
  33. },
  34. deleted: (Deleted value) async {
  35. emit(state.copyWith(isDeleted: true));
  36. },
  37. restore: (Restore value) async {
  38. emit(state.copyWith(isDeleted: false));
  39. },
  40. deletePermanently: (DeletePermanently value) async {
  41. final result = await trashService.deleteViews([Tuple2(view.id, TrashType.TrashView)]);
  42. final newState = result.fold((l) => state.copyWith(forceClose: true), (r) => state);
  43. emit(newState);
  44. },
  45. restorePage: (RestorePage value) async {
  46. final result = await trashService.putback(view.id);
  47. final newState = result.fold((l) => state.copyWith(isDeleted: false), (r) => state);
  48. emit(newState);
  49. },
  50. );
  51. });
  52. }
  53. @override
  54. Future<void> close() async {
  55. await listener.close();
  56. if (_subscription != null) {
  57. await _subscription?.cancel();
  58. }
  59. service.closeDocument(docId: view.id);
  60. return super.close();
  61. }
  62. Future<void> _initial(Initial value, Emitter<DocumentState> emit) async {
  63. listener.deletedNotifier.addPublishListener((result) {
  64. result.fold(
  65. (view) => add(const DocumentEvent.deleted()),
  66. (error) {},
  67. );
  68. });
  69. listener.restoredNotifier.addPublishListener((result) {
  70. result.fold(
  71. (view) => add(const DocumentEvent.restore()),
  72. (error) {},
  73. );
  74. });
  75. listener.start();
  76. final result = await service.openDocument(docId: view.id);
  77. result.fold(
  78. (block) {
  79. document = _decodeJsonToDocument(block.deltaStr);
  80. _subscription = document.changes.listen((event) {
  81. final delta = event.item2;
  82. final documentDelta = document.toDelta();
  83. _composeDelta(delta, documentDelta);
  84. });
  85. emit(state.copyWith(loadingState: DocumentLoadingState.finish(left(unit))));
  86. },
  87. (err) {
  88. emit(state.copyWith(loadingState: DocumentLoadingState.finish(right(err))));
  89. },
  90. );
  91. }
  92. // Document _decodeListToDocument(Uint8List data) {
  93. // final json = jsonDecode(utf8.decode(data));
  94. // final document = Document.fromJson(json);
  95. // return document;
  96. // }
  97. void _composeDelta(Delta composedDelta, Delta documentDelta) async {
  98. final json = jsonEncode(composedDelta.toJson());
  99. Log.debug("doc_id: $view.id - Send json: $json");
  100. final result = await service.composeDelta(docId: view.id, data: json);
  101. result.fold((rustDoc) {
  102. // final json = utf8.decode(doc.data);
  103. final rustDelta = Delta.fromJson(jsonDecode(rustDoc.deltaStr));
  104. if (documentDelta != rustDelta) {
  105. Log.error("Receive : $rustDelta");
  106. Log.error("Expected : $documentDelta");
  107. }
  108. }, (r) => null);
  109. }
  110. Document _decodeJsonToDocument(String data) {
  111. final json = jsonDecode(data);
  112. final document = Document.fromJson(json);
  113. return document;
  114. }
  115. }
  116. @freezed
  117. class DocumentEvent with _$DocumentEvent {
  118. const factory DocumentEvent.initial() = Initial;
  119. const factory DocumentEvent.deleted() = Deleted;
  120. const factory DocumentEvent.restore() = Restore;
  121. const factory DocumentEvent.restorePage() = RestorePage;
  122. const factory DocumentEvent.deletePermanently() = DeletePermanently;
  123. }
  124. @freezed
  125. class DocumentState with _$DocumentState {
  126. const factory DocumentState({
  127. required DocumentLoadingState loadingState,
  128. required bool isDeleted,
  129. required bool forceClose,
  130. }) = _DocumentState;
  131. factory DocumentState.initial() => const DocumentState(
  132. loadingState: _Loading(),
  133. isDeleted: false,
  134. forceClose: false,
  135. );
  136. }
  137. @freezed
  138. class DocumentLoadingState with _$DocumentLoadingState {
  139. const factory DocumentLoadingState.loading() = _Loading;
  140. const factory DocumentLoadingState.finish(Either<Unit, FlowyError> successOrFail) = _Finish;
  141. }