Browse Source

[flutter]: add scrollbar to trash list

appflowy 3 years ago
parent
commit
6e8ae0eca9

+ 23 - 1
app_flowy/lib/workspace/application/app/app_bloc.dart

@@ -10,7 +10,8 @@ part 'app_bloc.freezed.dart';
 
 class AppBloc extends Bloc<AppEvent, AppState> {
   final IApp iAppImpl;
-  AppBloc(this.iAppImpl) : super(AppState.initial());
+  final IAppListenr listener;
+  AppBloc({required this.iAppImpl, required this.listener}) : super(AppState.initial());
 
   @override
   Stream<AppState> mapEventToState(
@@ -18,6 +19,8 @@ class AppBloc extends Bloc<AppEvent, AppState> {
   ) async* {
     yield* event.map(
       initial: (e) async* {
+        listener.start(viewsChangeCallback: _handleViewsOrFail);
+
         yield* _fetchViews();
       },
       createView: (CreateView value) async* {
@@ -27,6 +30,24 @@ class AppBloc extends Bloc<AppEvent, AppState> {
           return state.copyWith(successOrFailure: right(error));
         });
       },
+      didReceiveViews: (e) async* {
+        yield state.copyWith(views: e.views);
+      },
+    );
+  }
+
+  @override
+  Future<void> close() async {
+    await listener.stop();
+    return super.close();
+  }
+
+  void _handleViewsOrFail(Either<List<View>, WorkspaceError> viewsOrFail) {
+    viewsOrFail.fold(
+      (views) => add(AppEvent.didReceiveViews(views)),
+      (error) {
+        Log.error(error);
+      },
     );
   }
 
@@ -46,6 +67,7 @@ class AppBloc extends Bloc<AppEvent, AppState> {
 class AppEvent with _$AppEvent {
   const factory AppEvent.initial() = Initial;
   const factory AppEvent.createView(String name, String desc, ViewType viewType) = CreateView;
+  const factory AppEvent.didReceiveViews(List<View> views) = ReceiveViews;
 }
 
 @freezed

+ 138 - 0
app_flowy/lib/workspace/application/app/app_bloc.freezed.dart

@@ -27,6 +27,12 @@ class _$AppEventTearOff {
       viewType,
     );
   }
+
+  ReceiveViews didReceiveViews(List<View> views) {
+    return ReceiveViews(
+      views,
+    );
+  }
 }
 
 /// @nodoc
@@ -39,12 +45,14 @@ mixin _$AppEvent {
     required TResult Function() initial,
     required TResult Function(String name, String desc, ViewType viewType)
         createView,
+    required TResult Function(List<View> views) didReceiveViews,
   }) =>
       throw _privateConstructorUsedError;
   @optionalTypeArgs
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
     TResult Function(String name, String desc, ViewType viewType)? createView,
+    TResult Function(List<View> views)? didReceiveViews,
     required TResult orElse(),
   }) =>
       throw _privateConstructorUsedError;
@@ -52,12 +60,14 @@ mixin _$AppEvent {
   TResult map<TResult extends Object?>({
     required TResult Function(Initial value) initial,
     required TResult Function(CreateView value) createView,
+    required TResult Function(ReceiveViews value) didReceiveViews,
   }) =>
       throw _privateConstructorUsedError;
   @optionalTypeArgs
   TResult maybeMap<TResult extends Object?>({
     TResult Function(Initial value)? initial,
     TResult Function(CreateView value)? createView,
+    TResult Function(ReceiveViews value)? didReceiveViews,
     required TResult orElse(),
   }) =>
       throw _privateConstructorUsedError;
@@ -118,6 +128,7 @@ class _$Initial implements Initial {
     required TResult Function() initial,
     required TResult Function(String name, String desc, ViewType viewType)
         createView,
+    required TResult Function(List<View> views) didReceiveViews,
   }) {
     return initial();
   }
@@ -127,6 +138,7 @@ class _$Initial implements Initial {
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
     TResult Function(String name, String desc, ViewType viewType)? createView,
+    TResult Function(List<View> views)? didReceiveViews,
     required TResult orElse(),
   }) {
     if (initial != null) {
@@ -140,6 +152,7 @@ class _$Initial implements Initial {
   TResult map<TResult extends Object?>({
     required TResult Function(Initial value) initial,
     required TResult Function(CreateView value) createView,
+    required TResult Function(ReceiveViews value) didReceiveViews,
   }) {
     return initial(this);
   }
@@ -149,6 +162,7 @@ class _$Initial implements Initial {
   TResult maybeMap<TResult extends Object?>({
     TResult Function(Initial value)? initial,
     TResult Function(CreateView value)? createView,
+    TResult Function(ReceiveViews value)? didReceiveViews,
     required TResult orElse(),
   }) {
     if (initial != null) {
@@ -250,6 +264,7 @@ class _$CreateView implements CreateView {
     required TResult Function() initial,
     required TResult Function(String name, String desc, ViewType viewType)
         createView,
+    required TResult Function(List<View> views) didReceiveViews,
   }) {
     return createView(name, desc, viewType);
   }
@@ -259,6 +274,7 @@ class _$CreateView implements CreateView {
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
     TResult Function(String name, String desc, ViewType viewType)? createView,
+    TResult Function(List<View> views)? didReceiveViews,
     required TResult orElse(),
   }) {
     if (createView != null) {
@@ -272,6 +288,7 @@ class _$CreateView implements CreateView {
   TResult map<TResult extends Object?>({
     required TResult Function(Initial value) initial,
     required TResult Function(CreateView value) createView,
+    required TResult Function(ReceiveViews value) didReceiveViews,
   }) {
     return createView(this);
   }
@@ -281,6 +298,7 @@ class _$CreateView implements CreateView {
   TResult maybeMap<TResult extends Object?>({
     TResult Function(Initial value)? initial,
     TResult Function(CreateView value)? createView,
+    TResult Function(ReceiveViews value)? didReceiveViews,
     required TResult orElse(),
   }) {
     if (createView != null) {
@@ -302,6 +320,126 @@ abstract class CreateView implements AppEvent {
       throw _privateConstructorUsedError;
 }
 
+/// @nodoc
+abstract class $ReceiveViewsCopyWith<$Res> {
+  factory $ReceiveViewsCopyWith(
+          ReceiveViews value, $Res Function(ReceiveViews) then) =
+      _$ReceiveViewsCopyWithImpl<$Res>;
+  $Res call({List<View> views});
+}
+
+/// @nodoc
+class _$ReceiveViewsCopyWithImpl<$Res> extends _$AppEventCopyWithImpl<$Res>
+    implements $ReceiveViewsCopyWith<$Res> {
+  _$ReceiveViewsCopyWithImpl(
+      ReceiveViews _value, $Res Function(ReceiveViews) _then)
+      : super(_value, (v) => _then(v as ReceiveViews));
+
+  @override
+  ReceiveViews get _value => super._value as ReceiveViews;
+
+  @override
+  $Res call({
+    Object? views = freezed,
+  }) {
+    return _then(ReceiveViews(
+      views == freezed
+          ? _value.views
+          : views // ignore: cast_nullable_to_non_nullable
+              as List<View>,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$ReceiveViews implements ReceiveViews {
+  const _$ReceiveViews(this.views);
+
+  @override
+  final List<View> views;
+
+  @override
+  String toString() {
+    return 'AppEvent.didReceiveViews(views: $views)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is ReceiveViews &&
+            (identical(other.views, views) ||
+                const DeepCollectionEquality().equals(other.views, views)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(views);
+
+  @JsonKey(ignore: true)
+  @override
+  $ReceiveViewsCopyWith<ReceiveViews> get copyWith =>
+      _$ReceiveViewsCopyWithImpl<ReceiveViews>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function(String name, String desc, ViewType viewType)
+        createView,
+    required TResult Function(List<View> views) didReceiveViews,
+  }) {
+    return didReceiveViews(views);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function(String name, String desc, ViewType viewType)? createView,
+    TResult Function(List<View> views)? didReceiveViews,
+    required TResult orElse(),
+  }) {
+    if (didReceiveViews != null) {
+      return didReceiveViews(views);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(Initial value) initial,
+    required TResult Function(CreateView value) createView,
+    required TResult Function(ReceiveViews value) didReceiveViews,
+  }) {
+    return didReceiveViews(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Initial value)? initial,
+    TResult Function(CreateView value)? createView,
+    TResult Function(ReceiveViews value)? didReceiveViews,
+    required TResult orElse(),
+  }) {
+    if (didReceiveViews != null) {
+      return didReceiveViews(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class ReceiveViews implements AppEvent {
+  const factory ReceiveViews(List<View> views) = _$ReceiveViews;
+
+  List<View> get views => throw _privateConstructorUsedError;
+  @JsonKey(ignore: true)
+  $ReceiveViewsCopyWith<ReceiveViews> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
 /// @nodoc
 class _$AppStateTearOff {
   const _$AppStateTearOff();

+ 1 - 0
app_flowy/lib/workspace/application/app/app_listen_bloc.dart

@@ -1,4 +1,5 @@
 import 'package:app_flowy/workspace/domain/i_app.dart';
+import 'package:flowy_log/flowy_log.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';

+ 6 - 1
app_flowy/lib/workspace/infrastructure/deps_resolver.dart

@@ -78,7 +78,12 @@ class HomeDepsResolver {
         (user, _) => MenuUserBloc(getIt<IUser>(param1: user), getIt<IUserListener>(param1: user)));
 
     // App
-    getIt.registerFactoryParam<AppBloc, String, void>((appId, _) => AppBloc(getIt<IApp>(param1: appId)));
+    getIt.registerFactoryParam<AppBloc, String, void>(
+      (appId, _) => AppBloc(
+        iAppImpl: getIt<IApp>(param1: appId),
+        listener: getIt<IAppListenr>(param1: appId),
+      ),
+    );
     getIt.registerFactoryParam<AppListenBloc, String, void>(
         (appId, _) => AppListenBloc(getIt<IAppListenr>(param1: appId)));
 

+ 26 - 7
app_flowy/lib/workspace/presentation/stack_page/trash/trash_page.dart

@@ -1,9 +1,13 @@
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/application/trash/trash_bloc.dart';
 import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
+import 'package:app_flowy/workspace/presentation/stack_page/trash/widget/sizes.dart';
 import 'package:app_flowy/workspace/presentation/stack_page/trash/widget/trash_cell.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
+import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
+import 'package:flowy_infra_ui/style_widget/scrolling/styled_scroll_bar.dart';
+import 'package:flowy_infra_ui/style_widget/scrolling/styled_scrollview.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/widget/spacing.dart';
@@ -29,7 +33,7 @@ class TrashStackContext extends HomeStackContext {
 
   @override
   Widget render() {
-    return const TrashStackPage(key: ObjectKey('TrashStackPage'));
+    return const TrashStackPage(key: ValueKey('TrashStackPage'));
   }
 
   @override
@@ -44,6 +48,7 @@ class TrashStackPage extends StatefulWidget {
 }
 
 class _TrashStackPageState extends State<TrashStackPage> {
+  final ScrollController _scrollController = ScrollController();
   @override
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
@@ -53,12 +58,26 @@ class _TrashStackPageState extends State<TrashStackPage> {
           _renderTopBar(theme),
           const VSpace(32),
           Expanded(
-            child: CustomScrollView(
-              controller: ScrollController(),
-              slivers: [
-                _renderListHeader(context),
-                _renderListBody(context),
-              ],
+            child: ScrollbarListStack(
+              axis: Axis.vertical,
+              controller: _scrollController,
+              barSize: 10,
+              child: StyledSingleChildScrollView(
+                controller: ScrollController(),
+                axis: Axis.horizontal,
+                child: SizedBox(
+                  width: TrashSizes.totalWidth,
+                  child: CustomScrollView(
+                    shrinkWrap: true,
+                    physics: StyledScrollPhysics(),
+                    controller: _scrollController,
+                    slivers: [
+                      _renderListHeader(context),
+                      _renderListBody(context),
+                    ],
+                  ),
+                ),
+              ),
             ),
           ),
         ],

+ 3 - 0
app_flowy/lib/workspace/presentation/stack_page/trash/widget/sizes.dart

@@ -3,4 +3,7 @@ class TrashSizes {
   static double get fileNameWidth => 320 * scale;
   static double get lashModifyWidth => 230 * scale;
   static double get createTimeWidth => 230 * scale;
+  static double get padding => 100 * scale;
+  static double get totalWidth =>
+      TrashSizes.fileNameWidth + TrashSizes.lashModifyWidth + TrashSizes.createTimeWidth + TrashSizes.padding;
 }

+ 6 - 31
app_flowy/lib/workspace/presentation/widgets/menu/widget/app/menu_app.dart

@@ -1,12 +1,10 @@
 import 'package:app_flowy/workspace/presentation/widgets/menu/widget/app/header.dart';
 import 'package:expandable/expandable.dart';
-import 'package:flowy_infra_ui/widget/error_page.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/application/app/app_bloc.dart';
-import 'package:app_flowy/workspace/application/app/app_listen_bloc.dart';
 import 'package:app_flowy/workspace/presentation/widgets/menu/menu.dart';
 import 'package:provider/provider.dart';
 import 'package:styled_widget/styled_widget.dart';
@@ -68,36 +66,13 @@ class MenuApp extends MenuItem {
           appBloc.add(const AppEvent.initial());
           return appBloc;
         }),
-        BlocProvider<AppListenBloc>(create: (context) {
-          final listener = getIt<AppListenBloc>(param1: appCtx.app.id);
-          listener.add(const AppListenEvent.started());
-          return listener;
-        }),
       ],
-      child: MultiBlocListener(
-        listeners: [
-          BlocListener<AppListenBloc, AppListenState>(
-            listenWhen: (p, c) => p != c,
-            listener: (context, state) => state.map(
-              initial: (_) => {},
-              didReceiveViews: (state) => appCtx.viewList.items = state.views,
-              loadFail: (s) => appCtx.viewList.items = [],
-            ),
-          ),
-        ],
-        child: BlocBuilder<AppListenBloc, AppListenState>(
-          builder: (context, state) {
-            final child = state.map(
-              initial: (_) => BlocBuilder<AppBloc, AppState>(builder: (context, state) {
-                appCtx.viewList.items = state.views ?? List.empty(growable: false);
-                return _renderViewSection(appCtx.viewList);
-              }),
-              didReceiveViews: (state) => _renderViewSection(appCtx.viewList),
-              loadFail: (s) => FlowyErrorPage(s.error.toString()),
-            );
-            return expandableWrapper(context, child);
-          },
-        ),
+      child: BlocBuilder<AppBloc, AppState>(
+        builder: (context, state) {
+          appCtx.viewList.items = state.views ?? List.empty(growable: false);
+          final child = _renderViewSection(appCtx.viewList);
+          return expandableWrapper(context, child);
+        },
       ),
     );
   }

+ 2 - 4
app_flowy/packages/flowy_infra_ui/lib/style_widget/scrolling/styled_scrollview.dart

@@ -23,12 +23,10 @@ class StyledSingleChildScrollView extends StatefulWidget {
   }) : super(key: key);
 
   @override
-  _StyledSingleChildScrollViewState createState() =>
-      _StyledSingleChildScrollViewState();
+  _StyledSingleChildScrollViewState createState() => _StyledSingleChildScrollViewState();
 }
 
-class _StyledSingleChildScrollViewState
-    extends State<StyledSingleChildScrollView> {
+class _StyledSingleChildScrollViewState extends State<StyledSingleChildScrollView> {
   late ScrollController scrollController;
 
   @override