doc_bloc.dart 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  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.stop();
  56. if (_subscription != null) {
  57. await _subscription?.cancel();
  58. }
  59. await service.closeDocument(docId: view.id);
  60. return super.close();
  61. }
  62. Future<void> _initial(Initial value, Emitter<DocumentState> emit) async {
  63. listener.start(
  64. onViewDeleted: (result) {
  65. result.fold(
  66. (view) => add(const DocumentEvent.deleted()),
  67. (error) {},
  68. );
  69. },
  70. onViewRestored: (result) {
  71. result.fold(
  72. (view) => add(const DocumentEvent.restore()),
  73. (error) {},
  74. );
  75. },
  76. );
  77. final result = await service.openDocument(docId: view.id);
  78. result.fold(
  79. (block) {
  80. document = _decodeJsonToDocument(block.deltaStr);
  81. _subscription = document.changes.listen((event) {
  82. final delta = event.item2;
  83. final documentDelta = document.toDelta();
  84. _composeDelta(delta, documentDelta);
  85. });
  86. emit(state.copyWith(loadingState: DocumentLoadingState.finish(left(unit))));
  87. },
  88. (err) {
  89. emit(state.copyWith(loadingState: DocumentLoadingState.finish(right(err))));
  90. },
  91. );
  92. }
  93. // Document _decodeListToDocument(Uint8List data) {
  94. // final json = jsonDecode(utf8.decode(data));
  95. // final document = Document.fromJson(json);
  96. // return document;
  97. // }
  98. void _composeDelta(Delta composedDelta, Delta documentDelta) async {
  99. final json = jsonEncode(composedDelta.toJson());
  100. Log.debug("doc_id: $view.id - Send json: $json");
  101. final result = await service.composeDelta(docId: view.id, data: json);
  102. result.fold((rustDoc) {
  103. // final json = utf8.decode(doc.data);
  104. final rustDelta = Delta.fromJson(jsonDecode(rustDoc.deltaStr));
  105. if (documentDelta != rustDelta) {
  106. Log.error("Receive : $rustDelta");
  107. Log.error("Expected : $documentDelta");
  108. }
  109. }, (r) => null);
  110. }
  111. Document _decodeJsonToDocument(String data) {
  112. final json = jsonDecode(data);
  113. final document = Document.fromJson(json);
  114. return document;
  115. }
  116. }
  117. @freezed
  118. class DocumentEvent with _$DocumentEvent {
  119. const factory DocumentEvent.initial() = Initial;
  120. const factory DocumentEvent.deleted() = Deleted;
  121. const factory DocumentEvent.restore() = Restore;
  122. const factory DocumentEvent.restorePage() = RestorePage;
  123. const factory DocumentEvent.deletePermanently() = DeletePermanently;
  124. }
  125. @freezed
  126. class DocumentState with _$DocumentState {
  127. const factory DocumentState({
  128. required DocumentLoadingState loadingState,
  129. required bool isDeleted,
  130. required bool forceClose,
  131. }) = _DocumentState;
  132. factory DocumentState.initial() => const DocumentState(
  133. loadingState: _Loading(),
  134. isDeleted: false,
  135. forceClose: false,
  136. );
  137. }
  138. @freezed
  139. class DocumentLoadingState with _$DocumentLoadingState {
  140. const factory DocumentLoadingState.loading() = _Loading;
  141. const factory DocumentLoadingState.finish(Either<Unit, FlowyError> successOrFail) = _Finish;
  142. }