view_bloc.dart 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  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. onViewChildViewsUpdated: (result) async {
  30. final view = await ViewBackendService.getView(
  31. result.parentViewId,
  32. );
  33. view.fold(
  34. (view) => add(ViewEvent.viewDidUpdate(left(view))),
  35. (error) => add(ViewEvent.viewDidUpdate(right(error))),
  36. );
  37. },
  38. );
  39. final isExpanded = await _getViewIsExpanded(view);
  40. await _loadViewsWhenExpanded(emit, isExpanded);
  41. },
  42. setIsEditing: (e) {
  43. emit(state.copyWith(isEditing: e.isEditing));
  44. },
  45. setIsExpanded: (e) async {
  46. if (e.isExpanded) {
  47. await _loadViewsWhenExpanded(emit, true);
  48. } else {
  49. emit(state.copyWith(isExpanded: e.isExpanded));
  50. }
  51. await _setViewIsExpanded(view, e.isExpanded);
  52. },
  53. viewDidUpdate: (e) {
  54. e.result.fold(
  55. (view) => emit(
  56. state.copyWith(
  57. view: view,
  58. childViews: view.childViews,
  59. successOrFailure: left(unit),
  60. ),
  61. ),
  62. (error) => emit(
  63. state.copyWith(successOrFailure: right(error)),
  64. ),
  65. );
  66. },
  67. rename: (e) async {
  68. final result = await ViewBackendService.updateView(
  69. viewId: view.id,
  70. name: e.newName,
  71. );
  72. emit(
  73. result.fold(
  74. (l) => state.copyWith(successOrFailure: left(unit)),
  75. (error) => state.copyWith(successOrFailure: right(error)),
  76. ),
  77. );
  78. },
  79. delete: (e) async {
  80. final result = await ViewBackendService.delete(viewId: view.id);
  81. emit(
  82. result.fold(
  83. (l) => state.copyWith(successOrFailure: left(unit)),
  84. (error) => state.copyWith(successOrFailure: right(error)),
  85. ),
  86. );
  87. },
  88. duplicate: (e) async {
  89. final result = await ViewBackendService.duplicate(view: view);
  90. emit(
  91. result.fold(
  92. (l) => state.copyWith(successOrFailure: left(unit)),
  93. (error) => state.copyWith(successOrFailure: right(error)),
  94. ),
  95. );
  96. },
  97. move: (value) async {
  98. final result = await ViewBackendService.moveViewV2(
  99. viewId: value.from.id,
  100. newParentId: value.newParentId,
  101. prevViewId: value.prevId,
  102. );
  103. emit(
  104. result.fold(
  105. (l) => state.copyWith(successOrFailure: left(unit)),
  106. (error) => state.copyWith(successOrFailure: right(error)),
  107. ),
  108. );
  109. },
  110. createView: (e) async {
  111. final result = await ViewBackendService.createView(
  112. parentViewId: view.id,
  113. name: e.name,
  114. desc: '',
  115. layoutType: e.layoutType,
  116. initialDataBytes: null,
  117. ext: {},
  118. openAfterCreate: e.openAfterCreated,
  119. );
  120. emit(
  121. result.fold(
  122. (view) => state.copyWith(
  123. lastCreatedView: view,
  124. successOrFailure: left(unit),
  125. ),
  126. (error) => state.copyWith(successOrFailure: right(error)),
  127. ),
  128. );
  129. },
  130. );
  131. });
  132. }
  133. @override
  134. Future<void> close() async {
  135. await listener.stop();
  136. return super.close();
  137. }
  138. Future<void> _loadViewsWhenExpanded(
  139. Emitter<ViewState> emit,
  140. bool isExpanded,
  141. ) async {
  142. if (!isExpanded) {
  143. return;
  144. }
  145. if (state.childViews.isNotEmpty) {
  146. // notify the old child views
  147. emit(
  148. state.copyWith(
  149. childViews: state.childViews,
  150. isExpanded: true,
  151. ),
  152. );
  153. }
  154. final viewsOrFailed =
  155. await ViewBackendService.getChildViews(viewId: state.view.id);
  156. viewsOrFailed.fold(
  157. (childViews) => emit(
  158. state.copyWith(
  159. childViews: childViews,
  160. isExpanded: true,
  161. ),
  162. ),
  163. (error) => emit(
  164. state.copyWith(
  165. successOrFailure: right(error),
  166. isExpanded: true,
  167. ),
  168. ),
  169. );
  170. }
  171. Future<void> _setViewIsExpanded(ViewPB view, bool isExpanded) async {
  172. final result = await getIt<KeyValueStorage>().get(KVKeys.expandedViews);
  173. final map = result.fold(
  174. (l) => {},
  175. (r) => jsonDecode(r),
  176. );
  177. if (isExpanded) {
  178. map[view.id] = true;
  179. } else {
  180. map.remove(view.id);
  181. }
  182. await getIt<KeyValueStorage>().set(KVKeys.expandedViews, jsonEncode(map));
  183. }
  184. Future<bool> _getViewIsExpanded(ViewPB view) {
  185. return getIt<KeyValueStorage>().get(KVKeys.expandedViews).then((result) {
  186. return result.fold((l) => false, (r) {
  187. final map = jsonDecode(r);
  188. return map[view.id] ?? false;
  189. });
  190. });
  191. }
  192. }
  193. @freezed
  194. class ViewEvent with _$ViewEvent {
  195. const factory ViewEvent.initial() = Initial;
  196. const factory ViewEvent.setIsEditing(bool isEditing) = SetEditing;
  197. const factory ViewEvent.setIsExpanded(bool isExpanded) = SetIsExpanded;
  198. const factory ViewEvent.rename(String newName) = Rename;
  199. const factory ViewEvent.delete() = Delete;
  200. const factory ViewEvent.duplicate() = Duplicate;
  201. const factory ViewEvent.move(
  202. ViewPB from,
  203. String newParentId,
  204. String? prevId,
  205. ) = Move;
  206. const factory ViewEvent.createView(
  207. String name,
  208. ViewLayoutPB layoutType, {
  209. /// open the view after created
  210. @Default(true) bool openAfterCreated,
  211. }) = CreateView;
  212. const factory ViewEvent.viewDidUpdate(Either<ViewPB, FlowyError> result) =
  213. ViewDidUpdate;
  214. }
  215. @freezed
  216. class ViewState with _$ViewState {
  217. const factory ViewState({
  218. required ViewPB view,
  219. required List<ViewPB> childViews,
  220. required bool isEditing,
  221. required bool isExpanded,
  222. required Either<Unit, FlowyError> successOrFailure,
  223. @Default(null) ViewPB? lastCreatedView,
  224. }) = _ViewState;
  225. factory ViewState.init(ViewPB view) => ViewState(
  226. view: view,
  227. childViews: view.childViews,
  228. isExpanded: false,
  229. isEditing: false,
  230. successOrFailure: left(unit),
  231. lastCreatedView: null,
  232. );
  233. }