소스 검색

[flutter]: customize home topbar action widget

appflowy 3 년 전
부모
커밋
b1668bfe6c

+ 37 - 27
app_flowy/lib/workspace/application/menu/menu_bloc.freezed.dart

@@ -24,7 +24,7 @@ class _$MenuEventTearOff {
     return const Collapse();
   }
 
-  OpenPage openPage(HomeStackContext<dynamic> context) {
+  OpenPage openPage(HomeStackContext<dynamic, dynamic> context) {
     return OpenPage(
       context,
     );
@@ -53,7 +53,8 @@ mixin _$MenuEvent {
   TResult when<TResult extends Object?>({
     required TResult Function() initial,
     required TResult Function() collapse,
-    required TResult Function(HomeStackContext<dynamic> context) openPage,
+    required TResult Function(HomeStackContext<dynamic, dynamic> context)
+        openPage,
     required TResult Function(String name, String? desc) createApp,
     required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
         didReceiveApps,
@@ -63,7 +64,7 @@ mixin _$MenuEvent {
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
     TResult Function()? collapse,
-    TResult Function(HomeStackContext<dynamic> context)? openPage,
+    TResult Function(HomeStackContext<dynamic, dynamic> context)? openPage,
     TResult Function(String name, String? desc)? createApp,
     TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
         didReceiveApps,
@@ -145,7 +146,8 @@ class _$_Initial implements _Initial {
   TResult when<TResult extends Object?>({
     required TResult Function() initial,
     required TResult Function() collapse,
-    required TResult Function(HomeStackContext<dynamic> context) openPage,
+    required TResult Function(HomeStackContext<dynamic, dynamic> context)
+        openPage,
     required TResult Function(String name, String? desc) createApp,
     required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
         didReceiveApps,
@@ -158,7 +160,7 @@ class _$_Initial implements _Initial {
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
     TResult Function()? collapse,
-    TResult Function(HomeStackContext<dynamic> context)? openPage,
+    TResult Function(HomeStackContext<dynamic, dynamic> context)? openPage,
     TResult Function(String name, String? desc)? createApp,
     TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
         didReceiveApps,
@@ -242,7 +244,8 @@ class _$Collapse implements Collapse {
   TResult when<TResult extends Object?>({
     required TResult Function() initial,
     required TResult Function() collapse,
-    required TResult Function(HomeStackContext<dynamic> context) openPage,
+    required TResult Function(HomeStackContext<dynamic, dynamic> context)
+        openPage,
     required TResult Function(String name, String? desc) createApp,
     required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
         didReceiveApps,
@@ -255,7 +258,7 @@ class _$Collapse implements Collapse {
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
     TResult Function()? collapse,
-    TResult Function(HomeStackContext<dynamic> context)? openPage,
+    TResult Function(HomeStackContext<dynamic, dynamic> context)? openPage,
     TResult Function(String name, String? desc)? createApp,
     TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
         didReceiveApps,
@@ -304,7 +307,7 @@ abstract class Collapse implements MenuEvent {
 abstract class $OpenPageCopyWith<$Res> {
   factory $OpenPageCopyWith(OpenPage value, $Res Function(OpenPage) then) =
       _$OpenPageCopyWithImpl<$Res>;
-  $Res call({HomeStackContext<dynamic> context});
+  $Res call({HomeStackContext<dynamic, dynamic> context});
 }
 
 /// @nodoc
@@ -324,7 +327,7 @@ class _$OpenPageCopyWithImpl<$Res> extends _$MenuEventCopyWithImpl<$Res>
       context == freezed
           ? _value.context
           : context // ignore: cast_nullable_to_non_nullable
-              as HomeStackContext<dynamic>,
+              as HomeStackContext<dynamic, dynamic>,
     ));
   }
 }
@@ -335,7 +338,7 @@ class _$OpenPage implements OpenPage {
   const _$OpenPage(this.context);
 
   @override
-  final HomeStackContext<dynamic> context;
+  final HomeStackContext<dynamic, dynamic> context;
 
   @override
   String toString() {
@@ -364,7 +367,8 @@ class _$OpenPage implements OpenPage {
   TResult when<TResult extends Object?>({
     required TResult Function() initial,
     required TResult Function() collapse,
-    required TResult Function(HomeStackContext<dynamic> context) openPage,
+    required TResult Function(HomeStackContext<dynamic, dynamic> context)
+        openPage,
     required TResult Function(String name, String? desc) createApp,
     required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
         didReceiveApps,
@@ -377,7 +381,7 @@ class _$OpenPage implements OpenPage {
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
     TResult Function()? collapse,
-    TResult Function(HomeStackContext<dynamic> context)? openPage,
+    TResult Function(HomeStackContext<dynamic, dynamic> context)? openPage,
     TResult Function(String name, String? desc)? createApp,
     TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
         didReceiveApps,
@@ -419,9 +423,11 @@ class _$OpenPage implements OpenPage {
 }
 
 abstract class OpenPage implements MenuEvent {
-  const factory OpenPage(HomeStackContext<dynamic> context) = _$OpenPage;
+  const factory OpenPage(HomeStackContext<dynamic, dynamic> context) =
+      _$OpenPage;
 
-  HomeStackContext<dynamic> get context => throw _privateConstructorUsedError;
+  HomeStackContext<dynamic, dynamic> get context =>
+      throw _privateConstructorUsedError;
   @JsonKey(ignore: true)
   $OpenPageCopyWith<OpenPage> get copyWith =>
       throw _privateConstructorUsedError;
@@ -502,7 +508,8 @@ class _$CreateApp implements CreateApp {
   TResult when<TResult extends Object?>({
     required TResult Function() initial,
     required TResult Function() collapse,
-    required TResult Function(HomeStackContext<dynamic> context) openPage,
+    required TResult Function(HomeStackContext<dynamic, dynamic> context)
+        openPage,
     required TResult Function(String name, String? desc) createApp,
     required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
         didReceiveApps,
@@ -515,7 +522,7 @@ class _$CreateApp implements CreateApp {
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
     TResult Function()? collapse,
-    TResult Function(HomeStackContext<dynamic> context)? openPage,
+    TResult Function(HomeStackContext<dynamic, dynamic> context)? openPage,
     TResult Function(String name, String? desc)? createApp,
     TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
         didReceiveApps,
@@ -633,7 +640,8 @@ class _$ReceiveApps implements ReceiveApps {
   TResult when<TResult extends Object?>({
     required TResult Function() initial,
     required TResult Function() collapse,
-    required TResult Function(HomeStackContext<dynamic> context) openPage,
+    required TResult Function(HomeStackContext<dynamic, dynamic> context)
+        openPage,
     required TResult Function(String name, String? desc) createApp,
     required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
         didReceiveApps,
@@ -646,7 +654,7 @@ class _$ReceiveApps implements ReceiveApps {
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
     TResult Function()? collapse,
-    TResult Function(HomeStackContext<dynamic> context)? openPage,
+    TResult Function(HomeStackContext<dynamic, dynamic> context)? openPage,
     TResult Function(String name, String? desc)? createApp,
     TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
         didReceiveApps,
@@ -706,7 +714,7 @@ class _$MenuStateTearOff {
       {required bool isCollapse,
       required Option<List<App>> apps,
       required Either<Unit, WorkspaceError> successOrFailure,
-      required HomeStackContext<dynamic> context}) {
+      required HomeStackContext<dynamic, dynamic> context}) {
     return _MenuState(
       isCollapse: isCollapse,
       apps: apps,
@@ -725,7 +733,8 @@ mixin _$MenuState {
   Option<List<App>> get apps => throw _privateConstructorUsedError;
   Either<Unit, WorkspaceError> get successOrFailure =>
       throw _privateConstructorUsedError;
-  HomeStackContext<dynamic> get context => throw _privateConstructorUsedError;
+  HomeStackContext<dynamic, dynamic> get context =>
+      throw _privateConstructorUsedError;
 
   @JsonKey(ignore: true)
   $MenuStateCopyWith<MenuState> get copyWith =>
@@ -740,7 +749,7 @@ abstract class $MenuStateCopyWith<$Res> {
       {bool isCollapse,
       Option<List<App>> apps,
       Either<Unit, WorkspaceError> successOrFailure,
-      HomeStackContext<dynamic> context});
+      HomeStackContext<dynamic, dynamic> context});
 }
 
 /// @nodoc
@@ -774,7 +783,7 @@ class _$MenuStateCopyWithImpl<$Res> implements $MenuStateCopyWith<$Res> {
       context: context == freezed
           ? _value.context
           : context // ignore: cast_nullable_to_non_nullable
-              as HomeStackContext<dynamic>,
+              as HomeStackContext<dynamic, dynamic>,
     ));
   }
 }
@@ -789,7 +798,7 @@ abstract class _$MenuStateCopyWith<$Res> implements $MenuStateCopyWith<$Res> {
       {bool isCollapse,
       Option<List<App>> apps,
       Either<Unit, WorkspaceError> successOrFailure,
-      HomeStackContext<dynamic> context});
+      HomeStackContext<dynamic, dynamic> context});
 }
 
 /// @nodoc
@@ -824,7 +833,7 @@ class __$MenuStateCopyWithImpl<$Res> extends _$MenuStateCopyWithImpl<$Res>
       context: context == freezed
           ? _value.context
           : context // ignore: cast_nullable_to_non_nullable
-              as HomeStackContext<dynamic>,
+              as HomeStackContext<dynamic, dynamic>,
     ));
   }
 }
@@ -845,7 +854,7 @@ class _$_MenuState implements _MenuState {
   @override
   final Either<Unit, WorkspaceError> successOrFailure;
   @override
-  final HomeStackContext<dynamic> context;
+  final HomeStackContext<dynamic, dynamic> context;
 
   @override
   String toString() {
@@ -887,7 +896,7 @@ abstract class _MenuState implements MenuState {
       {required bool isCollapse,
       required Option<List<App>> apps,
       required Either<Unit, WorkspaceError> successOrFailure,
-      required HomeStackContext<dynamic> context}) = _$_MenuState;
+      required HomeStackContext<dynamic, dynamic> context}) = _$_MenuState;
 
   @override
   bool get isCollapse => throw _privateConstructorUsedError;
@@ -897,7 +906,8 @@ abstract class _MenuState implements MenuState {
   Either<Unit, WorkspaceError> get successOrFailure =>
       throw _privateConstructorUsedError;
   @override
-  HomeStackContext<dynamic> get context => throw _privateConstructorUsedError;
+  HomeStackContext<dynamic, dynamic> get context =>
+      throw _privateConstructorUsedError;
   @override
   @JsonKey(ignore: true)
   _$MenuStateCopyWith<_MenuState> get copyWith =>

+ 3 - 1
app_flowy/lib/workspace/domain/page_stack/page_stack.dart

@@ -25,12 +25,14 @@ enum HomeStackType {
 
 List<HomeStackType> pages = HomeStackType.values.toList();
 
-abstract class HomeStackContext<T> with NavigationItem {
+abstract class HomeStackContext<T, S> with NavigationItem {
   List<NavigationItem> get navigationItems;
 
   @override
   Widget get naviTitle;
 
+  Widget? Function(BuildContext context) get buildNavigationActions;
+
   @override
   String get identifier;
 

+ 3 - 0
app_flowy/lib/workspace/presentation/stack_page/blank/blank_page.dart

@@ -11,6 +11,9 @@ class BlankStackContext extends HomeStackContext {
   @override
   Widget get naviTitle => const FlowyText.medium('Blank page', fontSize: 12);
 
+  @override
+  Widget? Function(BuildContext context) get buildNavigationActions => (_) => null;
+
   @override
   HomeStackType get type => HomeStackType.blank;
 

+ 119 - 7
app_flowy/lib/workspace/presentation/stack_page/doc/doc_stack_page.dart

@@ -2,13 +2,18 @@ import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/domain/i_view.dart';
 import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
 import 'package:app_flowy/workspace/domain/view_ext.dart';
+import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
+import 'package:flowy_infra/size.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
+import 'package:flowy_infra_ui/widget/rounded_button.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace-infra/view_create.pb.dart';
 import 'package:flutter/material.dart';
+import 'package:dartz/dartz.dart' as dartz;
 
 import 'doc_page.dart';
 
-class DocStackContext extends HomeStackContext {
+class DocStackContext extends HomeStackContext<String, ShareActionWrapper> {
   View _view;
   late IViewListener _listener;
   final ValueNotifier<String> _isUpdated = ValueNotifier<String>("");
@@ -29,15 +34,18 @@ class DocStackContext extends HomeStackContext {
 
   @override
   Widget get naviTitle => FlowyText.medium(_view.name, fontSize: 12);
+
+  @override
+  Widget? Function(BuildContext context) get buildNaviAction => _buildNaviAction;
+
   @override
   String get identifier => _view.id;
+
   @override
   HomeStackType get type => _view.stackType();
 
   @override
-  Widget buildWidget() {
-    return DocStackPage(_view, key: ValueKey(_view.id));
-  }
+  Widget buildWidget() => DocStackPage(_view, key: ValueKey(_view.id));
 
   @override
   List<NavigationItem> get navigationItems => _makeNavigationItems();
@@ -50,15 +58,52 @@ class DocStackContext extends HomeStackContext {
   //     }).toList();
 
   List<NavigationItem> _makeNavigationItems() {
-    return [
-      this,
-    ];
+    return [this];
   }
 
   @override
   void dispose() {
     _listener.stop();
   }
+
+  Widget _buildNaviAction(BuildContext context) {
+    return const DocShareButton();
+  }
+}
+
+class DocShareButton extends StatelessWidget {
+  const DocShareButton({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    double buttonWidth = 60;
+    return RoundedTextButton(
+      title: 'Share',
+      height: 30,
+      width: buttonWidth,
+      fontSize: 12,
+      borderRadius: Corners.s6Border,
+      color: Colors.lightBlue,
+      onPressed: () {
+        final actionList = ShareActions(onSelected: (result) {
+          result.fold(() {}, (action) {
+            switch (action) {
+              case ShareAction.markdown:
+                break;
+              case ShareAction.copyLink:
+                break;
+            }
+          });
+        });
+        actionList.show(
+          context,
+          context,
+          anchorDirection: AnchorDirection.bottomWithCenterAligned,
+          anchorOffset: Offset(-(buttonWidth / 2), 10),
+        );
+      },
+    );
+  }
 }
 
 class DocStackPage extends StatefulWidget {
@@ -90,3 +135,70 @@ class _DocStackPageState extends State<DocStackPage> {
     super.didUpdateWidget(oldWidget);
   }
 }
+
+class ShareActions with ActionList<ShareActionWrapper> implements FlowyOverlayDelegate {
+  final Function(dartz.Option<ShareAction>) onSelected;
+  final _items = ShareAction.values.map((action) => ShareActionWrapper(action)).toList();
+
+  ShareActions({
+    required this.onSelected,
+  });
+
+  @override
+  double get maxWidth => 130;
+
+  @override
+  double get itemHeight => 22;
+
+  @override
+  List<ShareActionWrapper> get items => _items;
+
+  @override
+  void Function(dartz.Option<ShareActionWrapper> p1) get selectCallback => (result) {
+        result.fold(
+          () => onSelected(dartz.none()),
+          (wrapper) => onSelected(
+            dartz.some(wrapper.inner),
+          ),
+        );
+      };
+
+  @override
+  FlowyOverlayDelegate? get delegate => this;
+
+  @override
+  void didRemove() {
+    onSelected(dartz.none());
+  }
+
+  @override
+  ListOverlayFooter? get footer => null;
+}
+
+enum ShareAction {
+  markdown,
+  copyLink,
+}
+
+class ShareActionWrapper extends ActionItem {
+  final ShareAction inner;
+
+  ShareActionWrapper(this.inner);
+
+  @override
+  Widget? get icon => null;
+
+  @override
+  String get name => inner.name;
+}
+
+extension QuestionBubbleExtension on ShareAction {
+  String get name {
+    switch (this) {
+      case ShareAction.markdown:
+        return "Markdown";
+      case ShareAction.copyLink:
+        return "Copy Link";
+    }
+  }
+}

+ 4 - 0
app_flowy/lib/workspace/presentation/stack_page/trash/trash_page.dart

@@ -3,6 +3,7 @@ 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:app_flowy/workspace/presentation/widgets/home_top_bar.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
@@ -27,6 +28,9 @@ class TrashStackContext extends HomeStackContext {
   @override
   Widget get naviTitle => const FlowyText.medium('Trash', fontSize: 12);
 
+  @override
+  Widget? Function(BuildContext context) get buildNavigationActions => (_) => null;
+
   @override
   HomeStackType get type => HomeStackType.trash;
 

+ 19 - 19
app_flowy/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart

@@ -22,21 +22,21 @@ class QuestionBubble extends StatelessWidget {
       height: 30,
       child: FlowyTextButton(
         '?',
-        tooltip: QuestionBubbleAction.values.map((action) => action.name).toList().join(','),
+        tooltip: BubbleAction.values.map((action) => action.name).toList().join(','),
         fontSize: 12,
         fontWeight: FontWeight.w600,
         fillColor: theme.selector,
         mainAxisAlignment: MainAxisAlignment.center,
         radius: BorderRadius.circular(10),
         onPressed: () {
-          final actionList = QuestionBubbleActions(onSelected: (result) {
+          final actionList = QuestionBubbleActionSheet(onSelected: (result) {
             result.fold(() {}, (action) {
               switch (action) {
-                case QuestionBubbleAction.whatsNews:
+                case BubbleAction.whatsNews:
                   // TODO: annie replace the URL with real ones
                   _launchURL("https://www.appflowy.io/whatsnew");
                   break;
-                case QuestionBubbleAction.help:
+                case BubbleAction.help:
                   // TODO: annie replace the URL with real ones
                   _launchURL("https://discord.gg/9Q2xaN37tV");
                   break;
@@ -63,11 +63,11 @@ class QuestionBubble extends StatelessWidget {
   }
 }
 
-class QuestionBubbleActions with ActionList<QuestionBubbleActionWrapper> implements FlowyOverlayDelegate {
-  final Function(dartz.Option<QuestionBubbleAction>) onSelected;
-  final _items = QuestionBubbleAction.values.map((action) => QuestionBubbleActionWrapper(action)).toList();
+class QuestionBubbleActionSheet with ActionList<BubbleActionWrapper> implements FlowyOverlayDelegate {
+  final Function(dartz.Option<BubbleAction>) onSelected;
+  final _items = BubbleAction.values.map((action) => BubbleActionWrapper(action)).toList();
 
-  QuestionBubbleActions({
+  QuestionBubbleActionSheet({
     required this.onSelected,
   });
 
@@ -78,10 +78,10 @@ class QuestionBubbleActions with ActionList<QuestionBubbleActionWrapper> impleme
   double get itemHeight => 22;
 
   @override
-  List<QuestionBubbleActionWrapper> get items => _items;
+  List<BubbleActionWrapper> get items => _items;
 
   @override
-  void Function(dartz.Option<QuestionBubbleActionWrapper> p1) get selectCallback => (result) {
+  void Function(dartz.Option<BubbleActionWrapper> p1) get selectCallback => (result) {
         result.fold(
           () => onSelected(dartz.none()),
           (wrapper) => onSelected(
@@ -145,15 +145,15 @@ class FlowyVersionDescription extends StatelessWidget {
   }
 }
 
-enum QuestionBubbleAction {
+enum BubbleAction {
   whatsNews,
   help,
 }
 
-class QuestionBubbleActionWrapper extends ActionItemData {
-  final QuestionBubbleAction inner;
+class BubbleActionWrapper extends ActionItem {
+  final BubbleAction inner;
 
-  QuestionBubbleActionWrapper(this.inner);
+  BubbleActionWrapper(this.inner);
   @override
   Widget? get icon => inner.emoji;
 
@@ -161,21 +161,21 @@ class QuestionBubbleActionWrapper extends ActionItemData {
   String get name => inner.name;
 }
 
-extension QuestionBubbleExtension on QuestionBubbleAction {
+extension QuestionBubbleExtension on BubbleAction {
   String get name {
     switch (this) {
-      case QuestionBubbleAction.whatsNews:
+      case BubbleAction.whatsNews:
         return "What's new?";
-      case QuestionBubbleAction.help:
+      case BubbleAction.help:
         return "Help & Support";
     }
   }
 
   Widget get emoji {
     switch (this) {
-      case QuestionBubbleAction.whatsNews:
+      case BubbleAction.whatsNews:
         return const Text('⭐️', style: TextStyle(fontSize: 12));
-      case QuestionBubbleAction.help:
+      case BubbleAction.help:
         return const Text('👥', style: TextStyle(fontSize: 12));
     }
   }

+ 10 - 18
app_flowy/lib/workspace/presentation/widgets/home_top_bar.dart

@@ -1,14 +1,14 @@
 import 'package:app_flowy/workspace/domain/image.dart';
+import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
 import 'package:app_flowy/workspace/presentation/home/home_sizes.dart';
 import 'package:app_flowy/workspace/presentation/home/navigation.dart';
-import 'package:flowy_infra/size.dart';
-import 'package:flowy_infra_ui/widget/rounded_button.dart';
 import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace-infra/view_create.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace-infra/view_create.pbenum.dart';
 import 'package:flutter/material.dart';
 import 'package:flowy_infra_ui/style_widget/extension.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
+import 'package:provider/provider.dart';
 
 class HomeTopBar extends StatelessWidget {
   const HomeTopBar({Key? key}) : super(key: key);
@@ -22,8 +22,14 @@ class HomeTopBar extends StatelessWidget {
         children: [
           const FlowyNavigation(),
           const Spacer(),
-          _renderShareButton(),
-          // _renderMoreButton(),
+          ChangeNotifierProvider.value(
+            value: Provider.of<HomeStackNotifier>(context, listen: false),
+            child: Consumer(
+              builder: (BuildContext context, HomeStackNotifier notifier, Widget? child) {
+                return notifier.stackContext.buildNavigationActions(context) ?? const SizedBox();
+              },
+            ),
+          ) // _renderMoreButton(),
         ],
       )
           .padding(
@@ -32,20 +38,6 @@ class HomeTopBar extends StatelessWidget {
           .bottomBorder(color: Colors.grey.shade300),
     );
   }
-
-  Widget _renderShareButton() {
-    return RoundedTextButton(
-      title: 'Share',
-      height: 30,
-      width: 60,
-      fontSize: 12,
-      borderRadius: Corners.s6Border,
-      color: Colors.lightBlue,
-      onPressed: () {
-        debugPrint('share page');
-      },
-    );
-  }
 }
 
 class HomeTitle extends StatelessWidget {

+ 1 - 2
app_flowy/lib/workspace/presentation/widgets/menu/widget/app/header/header.dart

@@ -5,7 +5,6 @@ import 'package:flowy_infra/flowy_icon_data_icons.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
-import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace-infra/app_create.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
@@ -79,7 +78,7 @@ class MenuAppHeader extends StatelessWidget {
           behavior: HitTestBehavior.opaque,
           onTap: () => ExpandableController.of(context, rebuildOnChange: false, required: true)?.toggle(),
           onSecondaryTap: () {
-            final actionList = AppDisclosureActions(onSelected: (action) => _handleAction(context, action));
+            final actionList = AppDisclosureActionSheet(onSelected: (action) => _handleAction(context, action));
             actionList.show(
               context,
               context,

+ 7 - 7
app_flowy/lib/workspace/presentation/widgets/menu/widget/app/header/right_click_action.dart

@@ -4,19 +4,19 @@ import 'package:dartz/dartz.dart' as dartz;
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flutter/material.dart';
 
-class AppDisclosureActions with ActionList<AppDisclosureActionWrapper> implements FlowyOverlayDelegate {
+class AppDisclosureActionSheet with ActionList<DisclosureActionWrapper> implements FlowyOverlayDelegate {
   final Function(dartz.Option<AppDisclosureAction>) onSelected;
-  final _items = AppDisclosureAction.values.map((action) => AppDisclosureActionWrapper(action)).toList();
+  final _items = AppDisclosureAction.values.map((action) => DisclosureActionWrapper(action)).toList();
 
-  AppDisclosureActions({
+  AppDisclosureActionSheet({
     required this.onSelected,
   });
 
   @override
-  List<AppDisclosureActionWrapper> get items => _items;
+  List<DisclosureActionWrapper> get items => _items;
 
   @override
-  void Function(dartz.Option<AppDisclosureActionWrapper> p1) get selectCallback => (result) {
+  void Function(dartz.Option<DisclosureActionWrapper> p1) get selectCallback => (result) {
         result.fold(
           () => onSelected(dartz.none()),
           (wrapper) => onSelected(
@@ -37,10 +37,10 @@ class AppDisclosureActions with ActionList<AppDisclosureActionWrapper> implement
   ListOverlayFooter? get footer => null;
 }
 
-class AppDisclosureActionWrapper extends ActionItemData {
+class DisclosureActionWrapper extends ActionItem {
   final AppDisclosureAction inner;
 
-  AppDisclosureActionWrapper(this.inner);
+  DisclosureActionWrapper(this.inner);
   @override
   Widget? get icon => inner.icon;
 

+ 1 - 1
app_flowy/lib/workspace/presentation/widgets/menu/widget/app/section/disclosure_action.dart

@@ -57,7 +57,7 @@ class ViewDisclosureButton extends StatelessWidget
   ListOverlayFooter? get footer => null;
 }
 
-class ViewDisclosureActionWrapper extends ActionItemData {
+class ViewDisclosureActionWrapper extends ActionItem {
   final ViewDisclosureAction inner;
 
   ViewDisclosureActionWrapper(this.inner);

+ 5 - 5
app_flowy/lib/workspace/presentation/widgets/pop_up_action.dart

@@ -8,7 +8,7 @@ import 'package:provider/provider.dart';
 import 'package:styled_widget/styled_widget.dart';
 import 'package:dartz/dartz.dart' as dartz;
 
-abstract class ActionList<T extends ActionItemData> {
+abstract class ActionList<T extends ActionItem> {
   List<T> get items;
 
   String get identifier => toString();
@@ -31,7 +31,7 @@ abstract class ActionList<T extends ActionItemData> {
   }) {
     final widgets = items
         .map(
-          (action) => ActionItem<T>(
+          (action) => ActionCell<T>(
             action: action,
             itemHeight: itemHeight,
             onSelected: (action) {
@@ -58,7 +58,7 @@ abstract class ActionList<T extends ActionItemData> {
   }
 }
 
-abstract class ActionItemData {
+abstract class ActionItem {
   Widget? get icon;
   String get name;
 }
@@ -69,11 +69,11 @@ class ActionListSizes {
   static double padding = 6;
 }
 
-class ActionItem<T extends ActionItemData> extends StatelessWidget {
+class ActionCell<T extends ActionItem> extends StatelessWidget {
   final T action;
   final Function(T) onSelected;
   final double itemHeight;
-  const ActionItem({
+  const ActionCell({
     Key? key,
     required this.action,
     required this.onSelected,

+ 4 - 4
rust-lib/flowy-workspace/src/handlers/view_handler.rs

@@ -17,7 +17,7 @@ use crate::{
 };
 use flowy_dispatch::prelude::{data_result, Data, DataResult, Unit};
 use flowy_document::entities::doc::DocDelta;
-use flowy_workspace_infra::entities::share::{ExportParams, ExportRequest};
+use flowy_workspace_infra::entities::share::{ExportData, ExportParams, ExportRequest};
 use std::{convert::TryInto, sync::Arc};
 
 pub(crate) async fn create_view_handler(
@@ -112,8 +112,8 @@ pub(crate) async fn duplicate_view_handler(
 pub(crate) async fn export_handler(
     data: Data<ExportRequest>,
     controller: Unit<Arc<ViewController>>,
-) -> Result<(), WorkspaceError> {
+) -> DataResult<ExportData, WorkspaceError> {
     let params: ExportParams = data.into_inner().try_into()?;
-    let _ = controller.export_doc(params.into()).await?;
-    Ok(())
+    let data = controller.export_doc(params.into()).await?;
+    data_result(data)
 }

+ 9 - 3
rust-lib/flowy-workspace/src/services/view_controller.rs

@@ -139,9 +139,15 @@ impl ViewController {
         Ok(())
     }
 
-    #[tracing::instrument(level = "debug", skip(self), err)]
-    pub(crate) async fn export_doc(&self, _params: ExportParams) -> Result<ExportData, WorkspaceError> {
-        unimplemented!()
+    #[tracing::instrument(level = "debug", skip(self, params), err)]
+    pub(crate) async fn export_doc(&self, params: ExportParams) -> Result<ExportData, WorkspaceError> {
+        let doc_identifier: DocIdentifier = params.doc_id.into();
+        let doc = self
+            .document
+            .read_document_data(doc_identifier, self.database.db_pool()?)
+            .await?;
+
+        Ok(ExportData { data: doc.data })
     }
 
     // belong_to_id will be the app_id or view_id.