Selaa lähdekoodia

chore: add move view event

appflowy 3 vuotta sitten
vanhempi
commit
25548ad9eb

+ 1 - 1
frontend/app_flowy/lib/startup/deps_resolver.dart

@@ -116,7 +116,7 @@ void _resolveFolderDeps(GetIt getIt) {
   getIt.registerFactoryParam<AppBloc, App, void>(
     (app, _) => AppBloc(
       app: app,
-      appService: AppService(),
+      appService: AppService(appId: app.id),
       appListener: AppListener(appId: app.id),
     ),
   );

+ 13 - 0
frontend/app_flowy/lib/workspace/application/app/app_service.dart

@@ -54,4 +54,17 @@ class AppService {
     }
     return FolderEventUpdateApp(request).send();
   }
+
+  Future<Either<Unit, FlowyError>> moveView({
+    required String viewId,
+    required int fromIndex,
+    required int toIndex,
+  }) {
+    UpdateAppPayload request = UpdateAppPayload.create()..appId = appId;
+
+    if (name != null) {
+      request.name = name;
+    }
+    return FolderEventUpdateApp(request).send();
+  }
 }

+ 37 - 12
frontend/app_flowy/lib/workspace/application/menu/menu_view_section_bloc.dart

@@ -1,6 +1,8 @@
 import 'dart:async';
 
 import 'package:app_flowy/workspace/application/app/app_bloc.dart';
+import 'package:app_flowy/workspace/application/app/app_service.dart';
+import 'package:flowy_sdk/log.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';
@@ -10,51 +12,73 @@ part 'menu_view_section_bloc.freezed.dart';
 class ViewSectionBloc extends Bloc<ViewSectionEvent, ViewSectionState> {
   void Function()? _viewsListener;
   void Function()? _selectedViewlistener;
-  final AppViewDataContext appViewData;
+  final AppViewDataContext _appViewData;
+  late final AppService _appService;
 
   ViewSectionBloc({
-    required this.appViewData,
-  }) : super(ViewSectionState.initial(appViewData)) {
+    required AppViewDataContext appViewData,
+  })  : _appService = AppService(appId: appViewData.appId),
+        _appViewData = 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));
-          }
+          _setSelectView(value, emit);
         },
         didReceiveViewUpdated: (_DidReceiveViewUpdated value) {
           emit(state.copyWith(views: value.views));
         },
+        moveView: (_MoveView value) async {
+          await _moveView(value);
+        },
       );
     });
   }
 
   void _startListening() {
-    _viewsListener = appViewData.addViewsChangeListener((views) {
+    _viewsListener = _appViewData.addViewsChangeListener((views) {
       if (!isClosed) {
         add(ViewSectionEvent.didReceiveViewUpdated(views));
       }
     });
-    _selectedViewlistener = appViewData.addSelectedViewChangeListener((view) {
+    _selectedViewlistener = _appViewData.addSelectedViewChangeListener((view) {
       if (!isClosed) {
         add(ViewSectionEvent.setSelectedView(view));
       }
     });
   }
 
+  void _setSelectView(_SetSelectedView value, Emitter<ViewSectionState> emit) {
+    if (state.views.contains(value.view)) {
+      emit(state.copyWith(selectedView: value.view));
+    } else {
+      emit(state.copyWith(selectedView: null));
+    }
+  }
+
+  Future<void> _moveView(_MoveView value) async {
+    if (value.fromIndex < state.views.length) {
+      final viewId = state.views[value.fromIndex].id;
+      final result = await _appService.moveView(
+        viewId: viewId,
+        fromIndex: value.fromIndex,
+        toIndex: value.toIndex,
+      );
+      result.fold((l) => null, (err) => Log.error(err));
+    }
+  }
+
   @override
   Future<void> close() async {
     if (_selectedViewlistener != null) {
-      appViewData.removeSelectedViewListener(_selectedViewlistener!);
+      _appViewData.removeSelectedViewListener(_selectedViewlistener!);
     }
 
     if (_viewsListener != null) {
-      appViewData.removeViewsListener(_viewsListener!);
+      _appViewData.removeViewsListener(_viewsListener!);
     }
 
     return super.close();
@@ -65,6 +89,7 @@ class ViewSectionBloc extends Bloc<ViewSectionEvent, ViewSectionState> {
 class ViewSectionEvent with _$ViewSectionEvent {
   const factory ViewSectionEvent.initial() = _Initial;
   const factory ViewSectionEvent.setSelectedView(View? view) = _SetSelectedView;
+  const factory ViewSectionEvent.moveView(int fromIndex, int toIndex) = _MoveView;
   const factory ViewSectionEvent.didReceiveViewUpdated(List<View> views) = _DidReceiveViewUpdated;
 }
 

+ 9 - 6
frontend/app_flowy/lib/workspace/presentation/home/menu/app/section/item.dart

@@ -40,12 +40,15 @@ class ViewSectionItem extends StatelessWidget {
       ],
       child: BlocBuilder<ViewBloc, ViewState>(
         builder: (context, state) {
-          return InkWell(
-            onTap: () => onSelected(context.read<ViewBloc>().state.view),
-            child: FlowyHover(
-              style: HoverStyle(hoverColor: theme.bg3),
-              builder: (_, onHover) => _render(context, onHover, state, theme.iconColor),
-              setSelected: () => state.isEditing || isSelected,
+          return Padding(
+            padding: const EdgeInsets.symmetric(vertical: 4),
+            child: InkWell(
+              onTap: () => onSelected(context.read<ViewBloc>().state.view),
+              child: FlowyHover(
+                style: HoverStyle(hoverColor: theme.bg3),
+                builder: (_, onHover) => _render(context, onHover, state, theme.iconColor),
+                setSelected: () => state.isEditing || isSelected,
+              ),
             ),
           );
         },

+ 31 - 178
frontend/app_flowy/lib/workspace/presentation/home/menu/app/section/section.dart

@@ -1,17 +1,14 @@
-import 'dart:async';
-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:flutter_bloc/flutter_bloc.dart';
 import 'package:reorderables/reorderables.dart';
-import 'package:styled_widget/styled_widget.dart';
+
 import 'item.dart';
 
 class ViewSection extends StatelessWidget {
@@ -26,192 +23,48 @@ class ViewSection extends StatelessWidget {
         bloc.add(const ViewSectionEvent.initial());
         return bloc;
       },
-      child: BlocBuilder<ViewSectionBloc, ViewSectionState>(
-        builder: (context, state) {
-          return _SectionItems(views: state.views);
+      child: BlocListener<ViewSectionBloc, ViewSectionState>(
+        listenWhen: (p, c) => p.selectedView != c.selectedView,
+        listener: (context, state) {
+          if (state.selectedView != null) {
+            WidgetsBinding.instance?.addPostFrameCallback((_) {
+              getIt<HomeStackManager>().setPlugin(state.selectedView!.plugin());
+            });
+          }
         },
+        child: BlocBuilder<ViewSectionBloc, ViewSectionState>(
+          builder: (context, state) {
+            return _reorderableColum(context, state);
+          },
+        ),
       ),
     );
   }
 
-  // Widget _renderSectionItems(BuildContext context, List<View> views) {
-  //   List<Widget> viewWidgets = [];
-  //   if (views.isNotEmpty) {
-  //     viewWidgets = views
-  //         .map(
-  //           (view) => ViewSectionItem(
-  //             view: view,
-  //             isSelected: _isViewSelected(context, view.id),
-  //             onSelected: (view) {
-  //               context.read<ViewSectionNotifier>().selectedView = view;
-  //               Provider.of<MenuSharedState>(context, listen: false).selectedView.value = view;
-  //             },
-  //           ).padding(vertical: 4),
-  //         )
-  //         .toList(growable: false);
-  //   }
-
-  //   return Column(children: viewWidgets);
-  // }
-
-  // bool _isViewSelected(BuildContext context, String viewId) {
-  //   final view = context.read<ViewSectionNotifier>().selectedView;
-  //   if (view == null) {
-  //     return false;
-  //   }
-  //   return view.id == viewId;
-  // }
-}
-
-class _SectionItems extends StatefulWidget {
-  const _SectionItems({Key? key, required this.views}) : super(key: key);
-
-  final List<View> views;
-
-  @override
-  State<_SectionItems> createState() => _SectionItemsState();
-}
-
-class _SectionItemsState extends State<_SectionItems> {
-  List<View> views = <View>[];
-
-  /// Maps the hasmap value of the section items to their index in the reorderable list.
-  //TODO @gaganyadav80: Retain this map to persist the order of the items.
-  final Map<String, int> _sectionItemIndex = <String, int>{};
-
-  void _initItemList() {
-    views.addAll(widget.views);
-
-    for (int i = 0; i < views.length; i++) {
-      if (_sectionItemIndex[views[i].id] == null) {
-        _sectionItemIndex[views[i].id] = i;
-      }
-    }
-  }
-
-  @override
-  void initState() {
-    super.initState();
-    _initItemList();
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    if (views.isEmpty) {
-      _initItemList();
-    }
+  ReorderableColumn _reorderableColum(BuildContext context, ViewSectionState state) {
+    final children = state.views.map((view) {
+      return ViewSectionItem(
+        key: ValueKey(view.id),
+        view: view,
+        isSelected: _isViewSelected(state, view.id),
+        onSelected: (view) => getIt<MenuSharedState>().latestOpenView = view,
+      );
+    }).toList();
 
-    log("BUILD: Section items: ${views.length}");
     return ReorderableColumn(
       needsLongPressDraggable: false,
       onReorder: (oldIndex, index) {
-        setState(() {
-          // int index = newIndex > oldIndex ? newIndex - 1 : newIndex;
-          View section = views.removeAt(oldIndex);
-          views.insert(index, section);
-
-          _sectionItemIndex[section.id] = index;
-        });
+        context.read<ViewSectionBloc>().add(ViewSectionEvent.moveView(oldIndex, index));
       },
-      children: List.generate(
-        views.length,
-        (index) {
-          return Container(
-            key: ValueKey(views[index].id),
-            child: views
-                .map(
-                  (view) => ViewSectionItem(
-                    view: view,
-                    isSelected: _isViewSelected(context, view.id),
-                    onSelected: (view) => getIt<MenuSharedState>().latestOpenView = view,
-                  ).padding(vertical: 4),
-                )
-                .toList()[index],
-          );
-        },
-      ),
+      children: children,
     );
   }
 
-  bool _isViewSelected(BuildContext context, String viewId) {
-    // final view = context.read<ViewSectionNotifier>().selectedView;
-    // if (view == null) {
-    //   return false;
-    // }
-    // return view.id == viewId;
-    return false;
-  }
-}
-
-class ViewSectionNotifier with ChangeNotifier {
-  bool isDisposed = false;
-  List<View> _views;
-  View? _selectedView;
-  Timer? _notifyListenerOperation;
-  VoidCallback? _latestViewDidChangeFn;
-
-  ViewSectionNotifier({
-    required List<View> views,
-    View? initialSelectedView,
-  })  : _views = views,
-        _selectedView = initialSelectedView {
-    _latestViewDidChangeFn = getIt<MenuSharedState>().addLatestViewListener((latestOpenView) {
-      if (_views.contains(latestOpenView)) {
-        selectedView = latestOpenView;
-      } else {
-        selectedView = null;
-      }
-    });
-  }
-
-  set views(List<View> views) {
-    if (_views != views) {
-      _views = views;
-      _notifyListeners();
-    }
-  }
-
-  List<View> get views => _views;
-
-  set selectedView(View? view) {
-    if (_selectedView == view) {
-      return;
-    }
-    _selectedView = view;
-    _notifyListeners();
-
-    if (view != null) {
-      WidgetsBinding.instance?.addPostFrameCallback((_) {
-        getIt<HomeStackManager>().setPlugin(view.plugin());
-      });
-    } else {
-      // do nothing
-    }
-  }
-
-  View? get selectedView => _selectedView;
-
-  void update(AppViewDataContext notifier) {
-    views = notifier.views;
-  }
-
-  void _notifyListeners() {
-    _notifyListenerOperation?.cancel();
-    _notifyListenerOperation = Timer(const Duration(milliseconds: 30), () {
-      if (!isDisposed) {
-        notifyListeners();
-      }
-    });
-  }
-
-  @override
-  void dispose() {
-    isDisposed = true;
-    _notifyListenerOperation?.cancel();
-    if (_latestViewDidChangeFn != null) {
-      getIt<MenuSharedState>().removeLatestViewListener(_latestViewDidChangeFn!);
-      _latestViewDidChangeFn = null;
+  bool _isViewSelected(ViewSectionState state, String viewId) {
+    final view = state.selectedView;
+    if (view == null) {
+      return false;
     }
-    super.dispose();
+    return view.id == viewId;
   }
 }