view_bloc.dart 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. import 'dart:convert';
  2. import 'package:appflowy/core/config/kv.dart';
  3. import 'package:appflowy/core/config/kv_keys.dart';
  4. import 'package:appflowy/startup/startup.dart';
  5. import 'package:appflowy/workspace/application/view/view_listener.dart';
  6. import 'package:appflowy/workspace/application/view/view_service.dart';
  7. import 'package:dartz/dartz.dart';
  8. import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
  9. import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
  10. import 'package:flutter_bloc/flutter_bloc.dart';
  11. import 'package:freezed_annotation/freezed_annotation.dart';
  12. part 'view_bloc.freezed.dart';
  13. class ViewBloc extends Bloc<ViewEvent, ViewState> {
  14. final ViewBackendService viewBackendSvc;
  15. final ViewListener listener;
  16. final ViewPB view;
  17. ViewBloc({
  18. required this.view,
  19. }) : viewBackendSvc = ViewBackendService(),
  20. listener = ViewListener(viewId: view.id),
  21. super(ViewState.init(view)) {
  22. on<ViewEvent>((event, emit) async {
  23. await event.map(
  24. initial: (e) async {
  25. listener.start(
  26. onViewUpdated: (result) {
  27. add(ViewEvent.viewDidUpdate(left(result)));
  28. },
  29. );
  30. final isExpanded = await _getViewIsExpanded(view);
  31. await _loadViewsWhenExpanded(emit, isExpanded);
  32. },
  33. setIsEditing: (e) {
  34. emit(state.copyWith(isEditing: e.isEditing));
  35. },
  36. setIsExpanded: (e) async {
  37. if (e.isExpanded) {
  38. await _loadViewsWhenExpanded(emit, true);
  39. } else {
  40. emit(state.copyWith(isExpanded: e.isExpanded));
  41. }
  42. await _setViewIsExpanded(view, e.isExpanded);
  43. },
  44. viewDidUpdate: (e) {
  45. e.result.fold(
  46. (view) => emit(
  47. state.copyWith(
  48. view: view,
  49. childViews: view.childViews,
  50. successOrFailure: left(unit),
  51. ),
  52. ),
  53. (error) => emit(
  54. state.copyWith(successOrFailure: right(error)),
  55. ),
  56. );
  57. },
  58. rename: (e) async {
  59. final result = await ViewBackendService.updateView(
  60. viewId: view.id,
  61. name: e.newName,
  62. );
  63. emit(
  64. result.fold(
  65. (l) => state.copyWith(successOrFailure: left(unit)),
  66. (error) => state.copyWith(successOrFailure: right(error)),
  67. ),
  68. );
  69. },
  70. delete: (e) async {
  71. final result = await ViewBackendService.delete(viewId: view.id);
  72. emit(
  73. result.fold(
  74. (l) => state.copyWith(successOrFailure: left(unit)),
  75. (error) => state.copyWith(successOrFailure: right(error)),
  76. ),
  77. );
  78. },
  79. duplicate: (e) async {
  80. final result = await ViewBackendService.duplicate(view: view);
  81. emit(
  82. result.fold(
  83. (l) => state.copyWith(successOrFailure: left(unit)),
  84. (error) => state.copyWith(successOrFailure: right(error)),
  85. ),
  86. );
  87. },
  88. move: (value) async {
  89. final result = await ViewBackendService.moveViewV2(
  90. viewId: value.from.id,
  91. newParentId: value.newParentId,
  92. prevViewId: value.prevId,
  93. );
  94. emit(
  95. result.fold(
  96. (l) => state.copyWith(successOrFailure: left(unit)),
  97. (error) => state.copyWith(successOrFailure: right(error)),
  98. ),
  99. );
  100. },
  101. createView: (e) async {
  102. final result = await ViewBackendService.createView(
  103. parentViewId: view.id,
  104. name: e.name,
  105. desc: '',
  106. layoutType: e.layoutType,
  107. initialDataBytes: null,
  108. ext: {},
  109. openAfterCreate: e.openAfterCreated,
  110. );
  111. emit(
  112. result.fold(
  113. (l) => state.copyWith(successOrFailure: left(unit)),
  114. (error) => state.copyWith(successOrFailure: right(error)),
  115. ),
  116. );
  117. },
  118. );
  119. });
  120. }
  121. @override
  122. Future<void> close() async {
  123. await listener.stop();
  124. return super.close();
  125. }
  126. Future<void> _loadViewsWhenExpanded(
  127. Emitter<ViewState> emit,
  128. bool isExpanded,
  129. ) async {
  130. if (!isExpanded) {
  131. return;
  132. }
  133. if (state.childViews.isNotEmpty) {
  134. // notify the old child views
  135. emit(
  136. state.copyWith(
  137. childViews: state.childViews,
  138. isExpanded: true,
  139. ),
  140. );
  141. }
  142. final viewsOrFailed =
  143. await ViewBackendService.getChildViews(viewId: state.view.id);
  144. viewsOrFailed.fold(
  145. (childViews) => emit(
  146. state.copyWith(
  147. childViews: childViews,
  148. isExpanded: true,
  149. ),
  150. ),
  151. (error) => emit(
  152. state.copyWith(
  153. successOrFailure: right(error),
  154. isExpanded: true,
  155. ),
  156. ),
  157. );
  158. }
  159. Future<void> _setViewIsExpanded(ViewPB view, bool isExpanded) async {
  160. final result = await getIt<KeyValueStorage>().get(KVKeys.expandedViews);
  161. final map = result.fold(
  162. (l) => {},
  163. (r) => jsonDecode(r),
  164. );
  165. if (isExpanded) {
  166. map[view.id] = true;
  167. } else {
  168. map.remove(view.id);
  169. }
  170. await getIt<KeyValueStorage>().set(KVKeys.expandedViews, jsonEncode(map));
  171. }
  172. Future<bool> _getViewIsExpanded(ViewPB view) {
  173. return getIt<KeyValueStorage>().get(KVKeys.expandedViews).then((result) {
  174. return result.fold((l) => false, (r) {
  175. final map = jsonDecode(r);
  176. return map[view.id] ?? false;
  177. });
  178. });
  179. }
  180. }
  181. @freezed
  182. class ViewEvent with _$ViewEvent {
  183. const factory ViewEvent.initial() = Initial;
  184. const factory ViewEvent.setIsEditing(bool isEditing) = SetEditing;
  185. const factory ViewEvent.setIsExpanded(bool isExpanded) = SetIsExpanded;
  186. const factory ViewEvent.rename(String newName) = Rename;
  187. const factory ViewEvent.delete() = Delete;
  188. const factory ViewEvent.duplicate() = Duplicate;
  189. const factory ViewEvent.move(
  190. ViewPB from,
  191. String newParentId,
  192. String? prevId,
  193. ) = Move;
  194. const factory ViewEvent.createView(
  195. String name,
  196. ViewLayoutPB layoutType, {
  197. /// open the view after created
  198. @Default(true) bool openAfterCreated,
  199. }) = CreateView;
  200. const factory ViewEvent.viewDidUpdate(Either<ViewPB, FlowyError> result) =
  201. ViewDidUpdate;
  202. }
  203. @freezed
  204. class ViewState with _$ViewState {
  205. const factory ViewState({
  206. required ViewPB view,
  207. required List<ViewPB> childViews,
  208. required bool isEditing,
  209. required bool isExpanded,
  210. required Either<Unit, FlowyError> successOrFailure,
  211. }) = _ViewState;
  212. factory ViewState.init(ViewPB view) => ViewState(
  213. view: view,
  214. childViews: view.childViews,
  215. isExpanded: false,
  216. isEditing: false,
  217. successOrFailure: left(unit),
  218. );
  219. }