Browse Source

refactor: replace provider with bloc in ViewSection

appflowy 3 năm trước cách đây
mục cha
commit
0b1f0ed401

+ 54 - 15
frontend/app_flowy/lib/workspace/application/app/app_bloc.dart

@@ -43,11 +43,19 @@ class AppBloc extends Bloc<AppEvent, AppState> {
     appListener.start(
       viewsChanged: (result) {
         result.fold(
-          (views) => add(AppEvent.didReceiveViewUpdated(views)),
+          (views) {
+            if (!isClosed) {
+              add(AppEvent.didReceiveViewUpdated(views));
+            }
+          },
           (error) => Log.error(error),
         );
       },
-      appUpdated: (app) => add(AppEvent.appDidUpdate(app)),
+      appUpdated: (app) {
+        if (!isClosed) {
+          add(AppEvent.appDidUpdate(app));
+        }
+      },
     );
   }
 
@@ -149,40 +157,73 @@ class AppState with _$AppState {
       );
 }
 
-class AppViewDataNotifier extends ChangeNotifier {
-  List<View> _views = [];
-  View? _selectedView;
+class AppViewDataContext extends ChangeNotifier {
+  final String appId;
+  final ValueNotifier<List<View>> _viewsNotifier = ValueNotifier([]);
+  final ValueNotifier<View?> _selectedViewNotifier = ValueNotifier(null);
   ExpandableController expandController = ExpandableController(initialExpanded: false);
 
-  AppViewDataNotifier() {
+  AppViewDataContext({required this.appId}) {
     _setLatestView(getIt<MenuSharedState>().latestOpenView);
     getIt<MenuSharedState>().addLatestViewListener((view) {
       _setLatestView(view);
     });
   }
 
+  VoidCallback addSelectedViewChangeListener(void Function(View?) callback) {
+    listener() {
+      callback(_selectedViewNotifier.value);
+    }
+
+    _selectedViewNotifier.addListener(listener);
+    return listener;
+  }
+
+  void removeSelectedViewListener(VoidCallback listener) {
+    _selectedViewNotifier.removeListener(listener);
+  }
+
   void _setLatestView(View? view) {
     view?.freeze();
-    _selectedView = view;
-    _expandIfNeed();
+
+    if (_selectedViewNotifier.value != view) {
+      _selectedViewNotifier.value = view;
+      _expandIfNeed();
+      notifyListeners();
+    }
   }
 
-  View? get selectedView => _selectedView;
+  View? get selectedView => _selectedViewNotifier.value;
 
   set views(List<View> views) {
-    if (_views != views) {
-      _views = views;
+    if (_viewsNotifier.value != views) {
+      _viewsNotifier.value = views;
       _expandIfNeed();
       notifyListeners();
     }
   }
 
+  UnmodifiableListView<View> get views => UnmodifiableListView(_viewsNotifier.value);
+
+  VoidCallback addViewsChangeListener(void Function(UnmodifiableListView<View>) callback) {
+    listener() {
+      callback(views);
+    }
+
+    _viewsNotifier.addListener(listener);
+    return listener;
+  }
+
+  void removeViewsListener(VoidCallback listener) {
+    _viewsNotifier.removeListener(listener);
+  }
+
   void _expandIfNeed() {
-    if (_selectedView == null) {
+    if (_selectedViewNotifier.value == null) {
       return;
     }
 
-    if (!_views.contains(_selectedView!)) {
+    if (!_viewsNotifier.value.contains(_selectedViewNotifier.value)) {
       return;
     }
 
@@ -193,6 +234,4 @@ class AppViewDataNotifier extends ChangeNotifier {
       });
     }
   }
-
-  UnmodifiableListView<View> get views => UnmodifiableListView(_views);
 }

+ 82 - 0
frontend/app_flowy/lib/workspace/application/menu/menu_view_section_bloc.dart

@@ -0,0 +1,82 @@
+import 'dart:async';
+
+import 'package:app_flowy/workspace/application/app/app_bloc.dart';
+import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+part 'menu_view_section_bloc.freezed.dart';
+
+class ViewSectionBloc extends Bloc<ViewSectionEvent, ViewSectionState> {
+  void Function()? _viewsListener;
+  void Function()? _selectedViewlistener;
+  final AppViewDataContext appViewData;
+
+  ViewSectionBloc({
+    required this.appViewData,
+  }) : super(ViewSectionState.initial(appViewData)) {
+    on<ViewSectionEvent>((event, emit) async {
+      await event.map(
+        initial: (e) async {
+          _startListening();
+        },
+        setSelectedView: (_SetSelectedView value) {
+          if (state.views.contains(value.view)) {
+            emit(state.copyWith(selectedView: value.view));
+          } else {
+            emit(state.copyWith(selectedView: null));
+          }
+        },
+        didReceiveViewUpdated: (_DidReceiveViewUpdated value) {
+          emit(state.copyWith(views: value.views));
+        },
+      );
+    });
+  }
+
+  void _startListening() {
+    _viewsListener = appViewData.addViewsChangeListener((views) {
+      if (!isClosed) {
+        add(ViewSectionEvent.didReceiveViewUpdated(views));
+      }
+    });
+    _selectedViewlistener = appViewData.addSelectedViewChangeListener((view) {
+      if (!isClosed) {
+        add(ViewSectionEvent.setSelectedView(view));
+      }
+    });
+  }
+
+  @override
+  Future<void> close() async {
+    if (_selectedViewlistener != null) {
+      appViewData.removeSelectedViewListener(_selectedViewlistener!);
+    }
+
+    if (_viewsListener != null) {
+      appViewData.removeViewsListener(_viewsListener!);
+    }
+
+    return super.close();
+  }
+}
+
+@freezed
+class ViewSectionEvent with _$ViewSectionEvent {
+  const factory ViewSectionEvent.initial() = _Initial;
+  const factory ViewSectionEvent.setSelectedView(View? view) = _SetSelectedView;
+  const factory ViewSectionEvent.didReceiveViewUpdated(List<View> views) = _DidReceiveViewUpdated;
+}
+
+@freezed
+class ViewSectionState with _$ViewSectionState {
+  const factory ViewSectionState({
+    required List<View> views,
+    View? selectedView,
+  }) = _ViewSectionState;
+
+  factory ViewSectionState.initial(AppViewDataContext appViewData) => ViewSectionState(
+        views: appViewData.views,
+        selectedView: appViewData.selectedView,
+      );
+}

+ 11 - 22
frontend/app_flowy/lib/workspace/presentation/home/menu/app/menu_app.dart

@@ -18,11 +18,11 @@ class MenuApp extends StatefulWidget {
 }
 
 class _MenuAppState extends State<MenuApp> {
-  late AppViewDataNotifier notifier;
+  late AppViewDataContext viewDataContext;
 
   @override
   void initState() {
-    notifier = AppViewDataNotifier();
+    viewDataContext = AppViewDataContext(appId: widget.app.id);
     super.initState();
   }
 
@@ -46,16 +46,16 @@ class _MenuAppState extends State<MenuApp> {
           ),
           BlocListener<AppBloc, AppState>(
             listenWhen: (p, c) => p.views != c.views,
-            listener: (context, state) => notifier.views = state.views,
+            listener: (context, state) => viewDataContext.views = state.views,
           ),
         ],
         child: BlocBuilder<AppBloc, AppState>(
           builder: (context, state) {
             return ChangeNotifierProvider.value(
-              value: notifier,
-              child: Consumer<AppViewDataNotifier>(
-                builder: (context, notifier, _) {
-                  return expandableWrapper(context, notifier);
+              value: viewDataContext,
+              child: Consumer<AppViewDataContext>(
+                builder: (context, viewDataContext, _) {
+                  return expandableWrapper(context, viewDataContext);
                 },
               ),
             );
@@ -65,9 +65,9 @@ class _MenuAppState extends State<MenuApp> {
     );
   }
 
-  ExpandableNotifier expandableWrapper(BuildContext context, AppViewDataNotifier notifier) {
+  ExpandableNotifier expandableWrapper(BuildContext context, AppViewDataContext viewDataContext) {
     return ExpandableNotifier(
-      controller: notifier.expandController,
+      controller: viewDataContext.expandController,
       child: ScrollOnExpand(
         scrollOnExpand: false,
         scrollOnCollapse: false,
@@ -86,7 +86,7 @@ class _MenuAppState extends State<MenuApp> {
                 value: Provider.of<AppearanceSettingModel>(context, listen: true),
                 child: MenuAppHeader(widget.app),
               ),
-              expanded: _renderViewSection(notifier),
+              expanded: ViewSection(appViewData: viewDataContext),
               collapsed: const SizedBox(),
             ),
           ],
@@ -95,20 +95,9 @@ class _MenuAppState extends State<MenuApp> {
     );
   }
 
-  Widget _renderViewSection(AppViewDataNotifier notifier) {
-    return MultiProvider(
-      providers: [ChangeNotifierProvider.value(value: notifier)],
-      child: Consumer(
-        builder: (context, AppViewDataNotifier notifier, child) {
-          return ViewSection(appData: notifier);
-        },
-      ),
-    );
-  }
-
   @override
   void dispose() {
-    notifier.dispose();
+    viewDataContext.dispose();
     super.dispose();
   }
 }

+ 21 - 22
frontend/app_flowy/lib/workspace/presentation/home/menu/app/section/section.dart

@@ -3,35 +3,34 @@ import 'dart:developer';
 
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/application/app/app_bloc.dart';
+import 'package:app_flowy/workspace/application/menu/menu_view_section_bloc.dart';
 import 'package:app_flowy/workspace/application/view/view_ext.dart';
 import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
 import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
 import 'package:flutter/material.dart';
-import 'package:provider/provider.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:reorderables/reorderables.dart';
 import 'package:styled_widget/styled_widget.dart';
 import 'item.dart';
 
 class ViewSection extends StatelessWidget {
-  final AppViewDataNotifier appData;
-  const ViewSection({Key? key, required this.appData}) : super(key: key);
+  final AppViewDataContext appViewData;
+  const ViewSection({Key? key, required this.appViewData}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
-    // The ViewSectionNotifier will be updated after AppDataNotifier changed passed by parent widget
-    return ChangeNotifierProxyProvider<AppViewDataNotifier, ViewSectionNotifier>(
-      create: (_) {
-        return ViewSectionNotifier(
-          context: context,
-          views: appData.views,
-          initialSelectedView: appData.selectedView,
-        );
+    return BlocProvider(
+      create: (context) {
+        final bloc = ViewSectionBloc(appViewData: appViewData);
+        bloc.add(const ViewSectionEvent.initial());
+        return bloc;
       },
-      update: (_, notifier, controller) => controller!..update(notifier),
-      child: Consumer(builder: (context, ViewSectionNotifier notifier, child) {
-        return _SectionItems(views: notifier.views);
-      }),
+      child: BlocBuilder<ViewSectionBloc, ViewSectionState>(
+        builder: (context, state) {
+          return _SectionItems(views: state.views);
+        },
+      ),
     );
   }
 
@@ -135,11 +134,12 @@ class _SectionItemsState extends State<_SectionItems> {
   }
 
   bool _isViewSelected(BuildContext context, String viewId) {
-    final view = context.read<ViewSectionNotifier>().selectedView;
-    if (view == null) {
-      return false;
-    }
-    return view.id == viewId;
+    // final view = context.read<ViewSectionNotifier>().selectedView;
+    // if (view == null) {
+    //   return false;
+    // }
+    // return view.id == viewId;
+    return false;
   }
 }
 
@@ -151,7 +151,6 @@ class ViewSectionNotifier with ChangeNotifier {
   VoidCallback? _latestViewDidChangeFn;
 
   ViewSectionNotifier({
-    required BuildContext context,
     required List<View> views,
     View? initialSelectedView,
   })  : _views = views,
@@ -192,7 +191,7 @@ class ViewSectionNotifier with ChangeNotifier {
 
   View? get selectedView => _selectedView;
 
-  void update(AppViewDataNotifier notifier) {
+  void update(AppViewDataContext notifier) {
     views = notifier.views;
   }
 

+ 8 - 11
frontend/app_flowy/lib/workspace/presentation/home/menu/menu.dart

@@ -13,7 +13,6 @@ import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder-data-model/workspace.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:provider/provider.dart';
 import 'package:styled_widget/styled_widget.dart';
 import 'package:expandable/expandable.dart';
 import 'package:flowy_infra/time/duration.dart';
@@ -199,9 +198,7 @@ class MenuSharedState {
   final ValueNotifier<View?> _latestOpenView = ValueNotifier<View?>(null);
 
   MenuSharedState({View? view}) {
-    if (view != null) {
-      _latestOpenView.value = view;
-    }
+    _latestOpenView.value = view;
   }
 
   View? get latestOpenView => _latestOpenView.value;
@@ -210,17 +207,17 @@ class MenuSharedState {
     _latestOpenView.value = view;
   }
 
-  VoidCallback addLatestViewListener(void Function(View?) latestViewDidChange) {
-    onChanged() {
-      latestViewDidChange(_latestOpenView.value);
+  VoidCallback addLatestViewListener(void Function(View?) callback) {
+    listener() {
+      callback(_latestOpenView.value);
     }
 
-    _latestOpenView.addListener(onChanged);
-    return onChanged;
+    _latestOpenView.addListener(listener);
+    return listener;
   }
 
-  void removeLatestViewListener(VoidCallback fn) {
-    _latestOpenView.removeListener(fn);
+  void removeLatestViewListener(VoidCallback listener) {
+    _latestOpenView.removeListener(listener);
   }
 }