view_bloc.dart 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  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. (view) => state.copyWith(
  114. lastCreatedView: view,
  115. successOrFailure: left(unit),
  116. ),
  117. (error) => state.copyWith(successOrFailure: right(error)),
  118. ),
  119. );
  120. },
  121. );
  122. });
  123. }
  124. @override
  125. Future<void> close() async {
  126. await listener.stop();
  127. return super.close();
  128. }
  129. Future<void> _loadViewsWhenExpanded(
  130. Emitter<ViewState> emit,
  131. bool isExpanded,
  132. ) async {
  133. if (!isExpanded) {
  134. return;
  135. }
  136. if (state.childViews.isNotEmpty) {
  137. // notify the old child views
  138. emit(
  139. state.copyWith(
  140. childViews: state.childViews,
  141. isExpanded: true,
  142. ),
  143. );
  144. }
  145. final viewsOrFailed =
  146. await ViewBackendService.getChildViews(viewId: state.view.id);
  147. viewsOrFailed.fold(
  148. (childViews) => emit(
  149. state.copyWith(
  150. childViews: childViews,
  151. isExpanded: true,
  152. ),
  153. ),
  154. (error) => emit(
  155. state.copyWith(
  156. successOrFailure: right(error),
  157. isExpanded: true,
  158. ),
  159. ),
  160. );
  161. }
  162. Future<void> _setViewIsExpanded(ViewPB view, bool isExpanded) async {
  163. final result = await getIt<KeyValueStorage>().get(KVKeys.expandedViews);
  164. final map = result.fold(
  165. (l) => {},
  166. (r) => jsonDecode(r),
  167. );
  168. if (isExpanded) {
  169. map[view.id] = true;
  170. } else {
  171. map.remove(view.id);
  172. }
  173. await getIt<KeyValueStorage>().set(KVKeys.expandedViews, jsonEncode(map));
  174. }
  175. Future<bool> _getViewIsExpanded(ViewPB view) {
  176. return getIt<KeyValueStorage>().get(KVKeys.expandedViews).then((result) {
  177. return result.fold((l) => false, (r) {
  178. final map = jsonDecode(r);
  179. return map[view.id] ?? false;
  180. });
  181. });
  182. }
  183. }
  184. @freezed
  185. class ViewEvent with _$ViewEvent {
  186. const factory ViewEvent.initial() = Initial;
  187. const factory ViewEvent.setIsEditing(bool isEditing) = SetEditing;
  188. const factory ViewEvent.setIsExpanded(bool isExpanded) = SetIsExpanded;
  189. const factory ViewEvent.rename(String newName) = Rename;
  190. const factory ViewEvent.delete() = Delete;
  191. const factory ViewEvent.duplicate() = Duplicate;
  192. const factory ViewEvent.move(
  193. ViewPB from,
  194. String newParentId,
  195. String? prevId,
  196. ) = Move;
  197. const factory ViewEvent.createView(
  198. String name,
  199. ViewLayoutPB layoutType, {
  200. /// open the view after created
  201. @Default(true) bool openAfterCreated,
  202. }) = CreateView;
  203. const factory ViewEvent.viewDidUpdate(Either<ViewPB, FlowyError> result) =
  204. ViewDidUpdate;
  205. }
  206. @freezed
  207. class ViewState with _$ViewState {
  208. const factory ViewState({
  209. required ViewPB view,
  210. required List<ViewPB> childViews,
  211. required bool isEditing,
  212. required bool isExpanded,
  213. required Either<Unit, FlowyError> successOrFailure,
  214. @Default(null) ViewPB? lastCreatedView,
  215. }) = _ViewState;
  216. factory ViewState.init(ViewPB view) => ViewState(
  217. view: view,
  218. childViews: view.childViews,
  219. isExpanded: false,
  220. isEditing: false,
  221. successOrFailure: left(unit),
  222. lastCreatedView: null,
  223. );
  224. }