[flutter]: customize home topbar action widget

This commit is contained in:
appflowy 2021-11-09 23:13:04 +08:00
parent 05f9f7b4a1
commit b1668bfe6c
13 changed files with 222 additions and 94 deletions

View file

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

View file

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

View file

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

View file

@ -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/i_view.dart';
import 'package:app_flowy/workspace/domain/page_stack/page_stack.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/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/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:flowy_sdk/protobuf/flowy-workspace-infra/view_create.pb.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:dartz/dartz.dart' as dartz;
import 'doc_page.dart'; import 'doc_page.dart';
class DocStackContext extends HomeStackContext { class DocStackContext extends HomeStackContext<String, ShareActionWrapper> {
View _view; View _view;
late IViewListener _listener; late IViewListener _listener;
final ValueNotifier<String> _isUpdated = ValueNotifier<String>(""); final ValueNotifier<String> _isUpdated = ValueNotifier<String>("");
@ -29,15 +34,18 @@ class DocStackContext extends HomeStackContext {
@override @override
Widget get naviTitle => FlowyText.medium(_view.name, fontSize: 12); Widget get naviTitle => FlowyText.medium(_view.name, fontSize: 12);
@override
Widget? Function(BuildContext context) get buildNaviAction => _buildNaviAction;
@override @override
String get identifier => _view.id; String get identifier => _view.id;
@override @override
HomeStackType get type => _view.stackType(); HomeStackType get type => _view.stackType();
@override @override
Widget buildWidget() { Widget buildWidget() => DocStackPage(_view, key: ValueKey(_view.id));
return DocStackPage(_view, key: ValueKey(_view.id));
}
@override @override
List<NavigationItem> get navigationItems => _makeNavigationItems(); List<NavigationItem> get navigationItems => _makeNavigationItems();
@ -50,15 +58,52 @@ class DocStackContext extends HomeStackContext {
// }).toList(); // }).toList();
List<NavigationItem> _makeNavigationItems() { List<NavigationItem> _makeNavigationItems() {
return [ return [this];
this,
];
} }
@override @override
void dispose() { void dispose() {
_listener.stop(); _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 { class DocStackPage extends StatefulWidget {
@ -90,3 +135,70 @@ class _DocStackPageState extends State<DocStackPage> {
super.didUpdateWidget(oldWidget); 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";
}
}
}

View file

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

View file

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

View file

@ -1,14 +1,14 @@
import 'package:app_flowy/workspace/domain/image.dart'; 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/home_sizes.dart';
import 'package:app_flowy/workspace/presentation/home/navigation.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_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.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace-infra/view_create.pbenum.dart'; import 'package:flowy_sdk/protobuf/flowy-workspace-infra/view_create.pbenum.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flowy_infra_ui/style_widget/extension.dart'; import 'package:flowy_infra_ui/style_widget/extension.dart';
import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:provider/provider.dart';
class HomeTopBar extends StatelessWidget { class HomeTopBar extends StatelessWidget {
const HomeTopBar({Key? key}) : super(key: key); const HomeTopBar({Key? key}) : super(key: key);
@ -22,8 +22,14 @@ class HomeTopBar extends StatelessWidget {
children: [ children: [
const FlowyNavigation(), const FlowyNavigation(),
const Spacer(), const Spacer(),
_renderShareButton(), ChangeNotifierProvider.value(
// _renderMoreButton(), 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( .padding(
@ -32,20 +38,6 @@ class HomeTopBar extends StatelessWidget {
.bottomBorder(color: Colors.grey.shade300), .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 { class HomeTitle extends StatelessWidget {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -139,9 +139,15 @@ impl ViewController {
Ok(()) Ok(())
} }
#[tracing::instrument(level = "debug", skip(self), err)] #[tracing::instrument(level = "debug", skip(self, params), err)]
pub(crate) async fn export_doc(&self, _params: ExportParams) -> Result<ExportData, WorkspaceError> { pub(crate) async fn export_doc(&self, params: ExportParams) -> Result<ExportData, WorkspaceError> {
unimplemented!() 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. // belong_to_id will be the app_id or view_id.