doc_bloc.dart 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import 'dart:convert';
  2. import 'package:app_flowy/plugins/trash/application/trash_service.dart';
  3. import 'package:app_flowy/workspace/application/view/view_listener.dart';
  4. import 'package:app_flowy/plugins/doc/application/doc_service.dart';
  5. import 'package:flowy_sdk/protobuf/flowy-folder/trash.pb.dart';
  6. import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
  7. import 'package:flowy_sdk/protobuf/flowy-folder/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 ViewPB 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
  42. .deleteViews([Tuple2(view.id, TrashType.TrashView)]);
  43. final newState = result.fold(
  44. (l) => state.copyWith(forceClose: true), (r) => state);
  45. emit(newState);
  46. },
  47. restorePage: (RestorePage value) async {
  48. final result = await trashService.putback(view.id);
  49. final newState = result.fold(
  50. (l) => state.copyWith(isDeleted: false), (r) => state);
  51. emit(newState);
  52. },
  53. );
  54. });
  55. }
  56. @override
  57. Future<void> close() async {
  58. await listener.stop();
  59. if (_subscription != null) {
  60. await _subscription?.cancel();
  61. }
  62. await service.closeDocument(docId: view.id);
  63. return super.close();
  64. }
  65. Future<void> _initial(Initial value, Emitter<DocumentState> emit) async {
  66. listener.start(
  67. onViewDeleted: (result) {
  68. result.fold(
  69. (view) => add(const DocumentEvent.deleted()),
  70. (error) {},
  71. );
  72. },
  73. onViewRestored: (result) {
  74. result.fold(
  75. (view) => add(const DocumentEvent.restore()),
  76. (error) {},
  77. );
  78. },
  79. );
  80. final result = await service.openDocument(docId: view.id);
  81. result.fold(
  82. (block) {
  83. document = _decodeJsonToDocument(block.snapshot);
  84. _subscription = document.changes.listen((event) {
  85. final delta = event.item2;
  86. final documentDelta = document.toDelta();
  87. _composeDelta(delta, documentDelta);
  88. });
  89. emit(state.copyWith(
  90. loadingState: DocumentLoadingState.finish(left(unit))));
  91. },
  92. (err) {
  93. emit(state.copyWith(
  94. loadingState: DocumentLoadingState.finish(right(err))));
  95. },
  96. );
  97. }
  98. // Document _decodeListToDocument(Uint8List data) {
  99. // final json = jsonDecode(utf8.decode(data));
  100. // final document = Document.fromJson(json);
  101. // return document;
  102. // }
  103. void _composeDelta(Delta composedDelta, Delta documentDelta) async {
  104. final json = jsonEncode(composedDelta.toJson());
  105. Log.debug("doc_id: $view.id - Send json: $json");
  106. final result = await service.applyEdit(docId: view.id, data: json);
  107. result.fold(
  108. (_) {},
  109. (r) => Log.error(r),
  110. );
  111. }
  112. Document _decodeJsonToDocument(String data) {
  113. final json = jsonDecode(data);
  114. final document = Document.fromJson(json);
  115. return document;
  116. }
  117. }
  118. @freezed
  119. class DocumentEvent with _$DocumentEvent {
  120. const factory DocumentEvent.initial() = Initial;
  121. const factory DocumentEvent.deleted() = Deleted;
  122. const factory DocumentEvent.restore() = Restore;
  123. const factory DocumentEvent.restorePage() = RestorePage;
  124. const factory DocumentEvent.deletePermanently() = DeletePermanently;
  125. }
  126. @freezed
  127. class DocumentState with _$DocumentState {
  128. const factory DocumentState({
  129. required DocumentLoadingState loadingState,
  130. required bool isDeleted,
  131. required bool forceClose,
  132. }) = _DocumentState;
  133. factory DocumentState.initial() => const DocumentState(
  134. loadingState: _Loading(),
  135. isDeleted: false,
  136. forceClose: false,
  137. );
  138. }
  139. @freezed
  140. class DocumentLoadingState with _$DocumentLoadingState {
  141. const factory DocumentLoadingState.loading() = _Loading;
  142. const factory DocumentLoadingState.finish(
  143. Either<Unit, FlowyError> successOrFail) = _Finish;
  144. }