| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 | 
							- import 'dart:collection';
 
- import 'package:appflowy/startup/plugin/plugin.dart';
 
- import 'package:appflowy/startup/startup.dart';
 
- import 'package:appflowy/workspace/application/app/app_listener.dart';
 
- import 'package:appflowy/workspace/application/app/app_service.dart';
 
- import 'package:appflowy/workspace/presentation/home/menu/menu.dart';
 
- import 'package:expandable/expandable.dart';
 
- import 'package:appflowy_backend/log.dart';
 
- import 'package:appflowy_backend/protobuf/flowy-folder/app.pb.dart';
 
- import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
 
- import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
 
- import 'package:flutter/foundation.dart';
 
- import 'package:freezed_annotation/freezed_annotation.dart';
 
- import 'package:flutter_bloc/flutter_bloc.dart';
 
- import 'package:dartz/dartz.dart';
 
- part 'app_bloc.freezed.dart';
 
- class AppBloc extends Bloc<AppEvent, AppState> {
 
-   final AppBackendService appService;
 
-   final AppListener appListener;
 
-   AppBloc({required AppPB app})
 
-       : appService = AppBackendService(),
 
-         appListener = AppListener(appId: app.id),
 
-         super(AppState.initial(app)) {
 
-     on<AppEvent>((event, emit) async {
 
-       await event.map(
 
-         initial: (e) async {
 
-           _startListening();
 
-           await _loadViews(emit);
 
-         },
 
-         createView: (CreateView value) async {
 
-           await _createView(value, emit);
 
-         },
 
-         loadViews: (_) async {
 
-           await _loadViews(emit);
 
-         },
 
-         delete: (e) async {
 
-           await _deleteApp(emit);
 
-         },
 
-         deleteView: (deletedView) async {
 
-           await _deleteView(emit, deletedView.viewId);
 
-         },
 
-         rename: (e) async {
 
-           await _renameView(e, emit);
 
-         },
 
-         appDidUpdate: (e) async {
 
-           final latestCreatedView = state.latestCreatedView;
 
-           final views = e.app.belongings.items;
 
-           AppState newState = state.copyWith(
 
-             views: views,
 
-             app: e.app,
 
-           );
 
-           if (latestCreatedView != null) {
 
-             final index = views
 
-                 .indexWhere((element) => element.id == latestCreatedView.id);
 
-             if (index == -1) {
 
-               newState = newState.copyWith(latestCreatedView: null);
 
-             }
 
-           }
 
-           emit(newState);
 
-         },
 
-       );
 
-     });
 
-   }
 
-   void _startListening() {
 
-     appListener.start(
 
-       onAppUpdated: (app) {
 
-         if (!isClosed) {
 
-           add(AppEvent.appDidUpdate(app));
 
-         }
 
-       },
 
-     );
 
-   }
 
-   Future<void> _renameView(Rename e, Emitter<AppState> emit) async {
 
-     final result =
 
-         await appService.updateApp(appId: state.app.id, name: e.newName);
 
-     result.fold(
 
-       (l) => emit(state.copyWith(successOrFailure: left(unit))),
 
-       (error) => emit(state.copyWith(successOrFailure: right(error))),
 
-     );
 
-   }
 
- // Delete the current app
 
-   Future<void> _deleteApp(Emitter<AppState> emit) async {
 
-     final result = await appService.delete(appId: state.app.id);
 
-     result.fold(
 
-       (unit) => emit(state.copyWith(successOrFailure: left(unit))),
 
-       (error) => emit(state.copyWith(successOrFailure: right(error))),
 
-     );
 
-   }
 
-   Future<void> _deleteView(Emitter<AppState> emit, String viewId) async {
 
-     final result = await appService.deleteView(viewId: viewId);
 
-     result.fold(
 
-       (unit) => emit(state.copyWith(successOrFailure: left(unit))),
 
-       (error) => emit(state.copyWith(successOrFailure: right(error))),
 
-     );
 
-   }
 
-   Future<void> _createView(CreateView value, Emitter<AppState> emit) async {
 
-     final result = await appService.createView(
 
-       appId: state.app.id,
 
-       name: value.name,
 
-       desc: value.desc ?? "",
 
-       layoutType: value.pluginBuilder.layoutType!,
 
-       initialData: value.initialData,
 
-       ext: value.ext ?? {},
 
-     );
 
-     result.fold(
 
-       (view) => emit(
 
-         state.copyWith(
 
-           latestCreatedView: view,
 
-           successOrFailure: left(unit),
 
-         ),
 
-       ),
 
-       (error) {
 
-         Log.error(error);
 
-         emit(state.copyWith(successOrFailure: right(error)));
 
-       },
 
-     );
 
-   }
 
-   @override
 
-   Future<void> close() async {
 
-     await appListener.stop();
 
-     return super.close();
 
-   }
 
-   Future<void> _loadViews(Emitter<AppState> emit) async {
 
-     final viewsOrFailed = await appService.getViews(appId: state.app.id);
 
-     viewsOrFailed.fold(
 
-       (views) => emit(state.copyWith(views: views)),
 
-       (error) {
 
-         Log.error(error);
 
-         emit(state.copyWith(successOrFailure: right(error)));
 
-       },
 
-     );
 
-   }
 
- }
 
- @freezed
 
- class AppEvent with _$AppEvent {
 
-   const factory AppEvent.initial() = Initial;
 
-   const factory AppEvent.createView(
 
-     String name,
 
-     PluginBuilder pluginBuilder, {
 
-     String? desc,
 
-     /// The initial data should be the JSON of the document
 
-     /// For example: {"document":{"type":"editor","children":[]}}
 
-     String? initialData,
 
-     Map<String, String>? ext,
 
-   }) = CreateView;
 
-   const factory AppEvent.loadViews() = LoadApp;
 
-   const factory AppEvent.delete() = DeleteApp;
 
-   const factory AppEvent.deleteView(String viewId) = DeleteView;
 
-   const factory AppEvent.rename(String newName) = Rename;
 
-   const factory AppEvent.appDidUpdate(AppPB app) = AppDidUpdate;
 
- }
 
- @freezed
 
- class AppState with _$AppState {
 
-   const factory AppState({
 
-     required AppPB app,
 
-     required List<ViewPB> views,
 
-     ViewPB? latestCreatedView,
 
-     required Either<Unit, FlowyError> successOrFailure,
 
-   }) = _AppState;
 
-   factory AppState.initial(AppPB app) => AppState(
 
-         app: app,
 
-         views: app.belongings.items,
 
-         successOrFailure: left(unit),
 
-       );
 
- }
 
- class AppViewDataContext extends ChangeNotifier {
 
-   final String appId;
 
-   final ValueNotifier<List<ViewPB>> _viewsNotifier = ValueNotifier([]);
 
-   final ValueNotifier<ViewPB?> _selectedViewNotifier = ValueNotifier(null);
 
-   VoidCallback? _menuSharedStateListener;
 
-   ExpandableController expandController =
 
-       ExpandableController(initialExpanded: false);
 
-   AppViewDataContext({required this.appId}) {
 
-     _setLatestView(getIt<MenuSharedState>().latestOpenView);
 
-     _menuSharedStateListener =
 
-         getIt<MenuSharedState>().addLatestViewListener((view) {
 
-       _setLatestView(view);
 
-     });
 
-   }
 
-   VoidCallback addSelectedViewChangeListener(void Function(ViewPB?) callback) {
 
-     listener() {
 
-       callback(_selectedViewNotifier.value);
 
-     }
 
-     _selectedViewNotifier.addListener(listener);
 
-     return listener;
 
-   }
 
-   void removeSelectedViewListener(VoidCallback listener) {
 
-     _selectedViewNotifier.removeListener(listener);
 
-   }
 
-   void _setLatestView(ViewPB? view) {
 
-     view?.freeze();
 
-     if (_selectedViewNotifier.value != view) {
 
-       _selectedViewNotifier.value = view;
 
-       _expandIfNeed();
 
-       notifyListeners();
 
-     }
 
-   }
 
-   ViewPB? get selectedView => _selectedViewNotifier.value;
 
-   set views(List<ViewPB> views) {
 
-     if (_viewsNotifier.value != views) {
 
-       _viewsNotifier.value = views;
 
-       _expandIfNeed();
 
-       notifyListeners();
 
-     }
 
-   }
 
-   UnmodifiableListView<ViewPB> get views =>
 
-       UnmodifiableListView(_viewsNotifier.value);
 
-   VoidCallback addViewsChangeListener(
 
-     void Function(UnmodifiableListView<ViewPB>) callback,
 
-   ) {
 
-     listener() {
 
-       callback(views);
 
-     }
 
-     _viewsNotifier.addListener(listener);
 
-     return listener;
 
-   }
 
-   void removeViewsListener(VoidCallback listener) {
 
-     _viewsNotifier.removeListener(listener);
 
-   }
 
-   void _expandIfNeed() {
 
-     if (_selectedViewNotifier.value == null) {
 
-       return;
 
-     }
 
-     if (!_viewsNotifier.value.contains(_selectedViewNotifier.value)) {
 
-       return;
 
-     }
 
-     if (expandController.expanded == false) {
 
-       // Workaround: Delay 150 milliseconds to make the smooth animation while expanding
 
-       Future.delayed(const Duration(milliseconds: 150), () {
 
-         expandController.expanded = true;
 
-       });
 
-     }
 
-   }
 
-   @override
 
-   void dispose() {
 
-     if (_menuSharedStateListener != null) {
 
-       getIt<MenuSharedState>()
 
-           .removeLatestViewListener(_menuSharedStateListener!);
 
-     }
 
-     super.dispose();
 
-   }
 
- }
 
 
  |