mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat:add toast in trash and confirm dialog + fix issues from launch review (#3787)
* chore: improve trash button * feat: improve restore all&delete all * refactor: add showFlowyMobileConfirmDialog * feat: add toast in delete/restore single file * refactor: refactor to TrashActionType enum * fix: text invisible in signin page in dark mode * feat: add FlowyMobileErrorStateContainer to display error state * refactor: add FlowyMobileStateContainer to handle empty or error state - Replace MobileErrorPage by FlowyMobileStateContainer.error - Implement app version in reporting issue on github - Implement FlowyMobileStateContainer in trash,setting,favorite and mobile view page * refactor: unify bottom sheet style - Unify bottom sheet style in add new page, page action, and trash action - Add icon color in BottomSheetActionWidget for future use - Add theme color in MobileBottomSheetDragHandle * chore: unify Appbar style * chore: remove the more button when trash list is empty * fix: show bottom sheet error * fix: fix merge and ui issue * refactor: refactor ViewPageBottomSheet and origanize code * chore: add icon color for favorite button * fix: add missing icon color and delete comments --------- Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
This commit is contained in:
parent
a93d325e6a
commit
73ea1a0685
@ -64,7 +64,6 @@ ThemeData getMobileThemeData(
|
||||
appBarTheme: AppBarTheme(
|
||||
foregroundColor: mobileColorTheme.onBackground,
|
||||
backgroundColor: mobileColorTheme.background,
|
||||
elevation: 80,
|
||||
centerTitle: false,
|
||||
titleTextStyle: TextStyle(
|
||||
color: mobileColorTheme.onBackground,
|
||||
@ -116,7 +115,7 @@ ThemeData getMobileThemeData(
|
||||
foregroundColor: MaterialStateProperty.all(
|
||||
mobileColorTheme.onBackground,
|
||||
),
|
||||
backgroundColor: MaterialStateProperty.all(Colors.white),
|
||||
backgroundColor: MaterialStateProperty.all(mobileColorTheme.background),
|
||||
shape: MaterialStateProperty.all(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
@ -129,7 +128,7 @@ ThemeData getMobileThemeData(
|
||||
),
|
||||
),
|
||||
padding: MaterialStateProperty.all(
|
||||
const EdgeInsets.symmetric(horizontal: 16),
|
||||
const EdgeInsets.symmetric(horizontal: 8, vertical: 12),
|
||||
),
|
||||
// splash color
|
||||
overlayColor: MaterialStateProperty.all(
|
||||
|
@ -1,8 +1,7 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_state_container.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_view_page.dart';
|
||||
import 'package:appflowy/mobile/presentation/error/error_page.dart';
|
||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
||||
@ -56,8 +55,11 @@ class _MobileViewPageState extends State<MobileViewPage> {
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
} else if (!state.hasData) {
|
||||
body = MobileErrorPage(
|
||||
message: LocaleKeys.error_loadingViewError.tr(),
|
||||
body = FlowyMobileStateContainer.error(
|
||||
emoji: '😔',
|
||||
title: LocaleKeys.error_weAreSorry.tr(),
|
||||
description: LocaleKeys.error_loadingViewError.tr(),
|
||||
errorMsg: state.error.toString(),
|
||||
);
|
||||
} else {
|
||||
body = state.data!.fold((view) {
|
||||
@ -65,8 +67,11 @@ class _MobileViewPageState extends State<MobileViewPage> {
|
||||
actions.add(_buildAppBarMoreButton(view));
|
||||
return view.plugin().widgetBuilder.buildWidget(shrinkWrap: false);
|
||||
}, (error) {
|
||||
return MobileErrorPage(
|
||||
message: error.toString(),
|
||||
return FlowyMobileStateContainer.error(
|
||||
emoji: '😔',
|
||||
title: LocaleKeys.error_weAreSorry.tr(),
|
||||
description: LocaleKeys.error_loadingViewError.tr(),
|
||||
errorMsg: error.toString(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -1,146 +1,9 @@
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_drag_handler.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_rename_widget.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_view_item_body.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_view_item_header.dart';
|
||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart' hide WidgetBuilder;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
Future<void> showMobileBottomSheet({
|
||||
required BuildContext context,
|
||||
required WidgetBuilder builder,
|
||||
}) async {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
enableDrag: true,
|
||||
useSafeArea: true,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(8.0),
|
||||
topRight: Radius.circular(8.0),
|
||||
),
|
||||
),
|
||||
builder: builder,
|
||||
);
|
||||
}
|
||||
|
||||
enum MobileBottomSheetType {
|
||||
view,
|
||||
rename,
|
||||
}
|
||||
|
||||
class MobileViewItemBottomSheet extends StatefulWidget {
|
||||
const MobileViewItemBottomSheet({
|
||||
super.key,
|
||||
required this.view,
|
||||
this.defaultType = MobileBottomSheetType.view,
|
||||
});
|
||||
|
||||
final ViewPB view;
|
||||
final MobileBottomSheetType defaultType;
|
||||
|
||||
@override
|
||||
State<MobileViewItemBottomSheet> createState() =>
|
||||
_MobileViewItemBottomSheetState();
|
||||
}
|
||||
|
||||
class _MobileViewItemBottomSheetState extends State<MobileViewItemBottomSheet> {
|
||||
MobileBottomSheetType type = MobileBottomSheetType.view;
|
||||
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
|
||||
type = widget.defaultType;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// drag handler
|
||||
const MobileBottomSheetDragHandler(),
|
||||
|
||||
// header
|
||||
_buildHeader(),
|
||||
const VSpace(8.0),
|
||||
const Divider(),
|
||||
|
||||
// body
|
||||
_buildBody(),
|
||||
const VSpace(12.0),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
switch (type) {
|
||||
case MobileBottomSheetType.view:
|
||||
case MobileBottomSheetType.rename:
|
||||
// header
|
||||
return MobileViewItemBottomSheetHeader(
|
||||
showBackButton: type != MobileBottomSheetType.view,
|
||||
view: widget.view,
|
||||
onBack: () {
|
||||
setState(() {
|
||||
type = MobileBottomSheetType.view;
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildBody() {
|
||||
switch (type) {
|
||||
case MobileBottomSheetType.view:
|
||||
return MobileViewItemBottomSheetBody(
|
||||
isFavorite: widget.view.isFavorite,
|
||||
onAction: (action) {
|
||||
switch (action) {
|
||||
case MobileViewItemBottomSheetBodyAction.rename:
|
||||
setState(() {
|
||||
type = MobileBottomSheetType.rename;
|
||||
});
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.duplicate:
|
||||
context.pop();
|
||||
context.read<ViewBloc>().add(const ViewEvent.duplicate());
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.share:
|
||||
// unimplemented
|
||||
context.pop();
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.delete:
|
||||
context.pop();
|
||||
context.read<ViewBloc>().add(const ViewEvent.delete());
|
||||
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.addToFavorites:
|
||||
case MobileViewItemBottomSheetBodyAction.removeFromFavorites:
|
||||
context.pop();
|
||||
context
|
||||
.read<FavoriteBloc>()
|
||||
.add(FavoriteEvent.toggle(widget.view));
|
||||
break;
|
||||
}
|
||||
},
|
||||
);
|
||||
case MobileBottomSheetType.rename:
|
||||
return MobileBottomSheetRenameWidget(
|
||||
name: widget.view.name,
|
||||
onRename: (name) {
|
||||
if (name != widget.view.name) {
|
||||
context.read<ViewBloc>().add(ViewEvent.rename(name));
|
||||
}
|
||||
context.pop();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
export 'bottom_sheet_action_widget.dart';
|
||||
export 'bottom_sheet_add_new_page.dart';
|
||||
export 'bottom_sheet_drag_handler.dart';
|
||||
export 'bottom_sheet_rename_widget.dart';
|
||||
export 'bottom_sheet_view_item_body.dart';
|
||||
export 'bottom_sheet_view_item_header.dart';
|
||||
export 'bottom_sheet_view_page.dart';
|
||||
export 'default_mobile_action_pane.dart';
|
||||
export 'show_mobile_bottom_sheet.dart';
|
||||
|
@ -1,8 +1,5 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/box_container.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class BottomSheetActionWidget extends StatelessWidget {
|
||||
const BottomSheetActionWidget({
|
||||
@ -10,42 +7,31 @@ class BottomSheetActionWidget extends StatelessWidget {
|
||||
required this.svg,
|
||||
required this.text,
|
||||
required this.onTap,
|
||||
this.iconColor,
|
||||
});
|
||||
|
||||
final FlowySvgData svg;
|
||||
final String text;
|
||||
final VoidCallback onTap;
|
||||
final Color? iconColor;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlowyBoxContainer(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
HapticFeedback.mediumImpact();
|
||||
onTap();
|
||||
},
|
||||
enableFeedback: true,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10.0,
|
||||
horizontal: 12.0,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
FlowySvg(
|
||||
svg,
|
||||
size: const Size.square(24.0),
|
||||
blendMode: BlendMode.dst,
|
||||
),
|
||||
const HSpace(6.0),
|
||||
FlowyText(text),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
),
|
||||
final iconColor =
|
||||
this.iconColor ?? Theme.of(context).colorScheme.onBackground;
|
||||
|
||||
return OutlinedButton.icon(
|
||||
icon: FlowySvg(
|
||||
svg,
|
||||
size: const Size.square(22.0),
|
||||
color: iconColor,
|
||||
),
|
||||
label: Text(text),
|
||||
style: Theme.of(context)
|
||||
.outlinedButtonTheme
|
||||
.style
|
||||
?.copyWith(alignment: Alignment.centerLeft),
|
||||
onPressed: onTap,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_action_widget.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_drag_handler.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_view_item_header.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
@ -21,46 +19,9 @@ class AddNewPageWidgetBottomSheet extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// drag handler
|
||||
const MobileBottomSheetDragHandler(),
|
||||
|
||||
// header
|
||||
MobileViewItemBottomSheetHeader(
|
||||
showBackButton: false,
|
||||
view: view,
|
||||
onBack: () {},
|
||||
),
|
||||
const VSpace(8.0),
|
||||
const Divider(),
|
||||
|
||||
// body
|
||||
_AddNewPageBody(
|
||||
onAction: onAction,
|
||||
),
|
||||
const VSpace(24.0),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AddNewPageBody extends StatelessWidget {
|
||||
const _AddNewPageBody({
|
||||
required this.onAction,
|
||||
});
|
||||
|
||||
final void Function(ViewLayoutPB layout) onAction;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
// rename, duplicate
|
||||
// new document, new grid
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
@ -69,6 +30,7 @@ class _AddNewPageBody extends StatelessWidget {
|
||||
onTap: () => onAction(ViewLayoutPB.Document),
|
||||
),
|
||||
),
|
||||
const HSpace(8),
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.grid_s,
|
||||
@ -78,10 +40,10 @@ class _AddNewPageBody extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
const VSpace(8),
|
||||
|
||||
// share, delete
|
||||
// new board, new calendar
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
@ -90,6 +52,7 @@ class _AddNewPageBody extends StatelessWidget {
|
||||
onTap: () => onAction(ViewLayoutPB.Board),
|
||||
),
|
||||
),
|
||||
const HSpace(8),
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.date_s,
|
||||
|
@ -12,7 +12,7 @@ class MobileBottomSheetDragHandler extends StatelessWidget {
|
||||
height: 4,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(2.0),
|
||||
color: Theme.of(context).colorScheme.onSecondary,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -2,6 +2,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_action_widget.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum MobileViewItemBottomSheetBodyAction {
|
||||
@ -26,12 +27,11 @@ class MobileViewItemBottomSheetBody extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// rename, duplicate
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
@ -42,6 +42,7 @@ class MobileViewItemBottomSheetBody extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
const HSpace(8),
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_duplicate_m,
|
||||
@ -53,10 +54,11 @@ class MobileViewItemBottomSheetBody extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
const VSpace(8),
|
||||
|
||||
// share, delete
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
@ -67,6 +69,7 @@ class MobileViewItemBottomSheetBody extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
const HSpace(8),
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_delete_m,
|
||||
@ -78,13 +81,15 @@ class MobileViewItemBottomSheetBody extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
const VSpace(8),
|
||||
|
||||
// remove from favorites
|
||||
|
||||
// remove from favorites/add to favorites
|
||||
BottomSheetActionWidget(
|
||||
svg: isFavorite
|
||||
? FlowySvgs.m_favorite_selected_lg
|
||||
: FlowySvgs.m_favorite_unselected_lg,
|
||||
//TODO(yijing): switch to theme color
|
||||
iconColor: isFavorite ? Colors.yellow : null,
|
||||
text: isFavorite
|
||||
? LocaleKeys.button_removeFromFavorites.tr()
|
||||
: LocaleKeys.button_addToFavorites.tr(),
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class MobileViewItemBottomSheetHeader extends StatelessWidget {
|
||||
const MobileViewItemBottomSheetHeader({
|
||||
@ -16,8 +16,8 @@ class MobileViewItemBottomSheetHeader extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
// back button,
|
||||
showBackButton
|
||||
@ -31,14 +31,23 @@ class MobileViewItemBottomSheetHeader extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
)
|
||||
: const HSpace(40.0),
|
||||
: const SizedBox.shrink(),
|
||||
// title
|
||||
FlowyText.regular(
|
||||
view.name,
|
||||
fontSize: 16.0,
|
||||
Expanded(
|
||||
child: Text(
|
||||
view.name,
|
||||
style: theme.textTheme.labelSmall,
|
||||
),
|
||||
),
|
||||
// placeholder, ensure the title is centered
|
||||
const HSpace(40.0),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.close,
|
||||
color: theme.hintColor,
|
||||
),
|
||||
onPressed: () {
|
||||
context.pop();
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -1,10 +1,6 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_action_widget.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_drag_handler.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_rename_widget.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_view_item.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_view_item_header.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
@ -47,21 +43,18 @@ class _ViewPageBottomSheetState extends State<ViewPageBottomSheet> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// drag handler
|
||||
const MobileBottomSheetDragHandler(),
|
||||
|
||||
// header
|
||||
_buildHeader(),
|
||||
const VSpace(8.0),
|
||||
const Divider(),
|
||||
|
||||
// body
|
||||
_buildBody(),
|
||||
const VSpace(24.0),
|
||||
],
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 32),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// header
|
||||
_buildHeader(),
|
||||
const VSpace(16),
|
||||
// body
|
||||
_buildBody(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -125,12 +118,11 @@ class MobileViewBottomSheetBody extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final isFavorite = view.isFavorite;
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// undo, redo
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
@ -141,6 +133,7 @@ class MobileViewBottomSheetBody extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
const HSpace(8),
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_redo_m,
|
||||
@ -152,10 +145,11 @@ class MobileViewBottomSheetBody extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
const VSpace(8),
|
||||
|
||||
// rename, duplicate
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
@ -166,6 +160,7 @@ class MobileViewBottomSheetBody extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
const HSpace(8),
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_duplicate_m,
|
||||
@ -177,10 +172,11 @@ class MobileViewBottomSheetBody extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
const VSpace(8),
|
||||
|
||||
// share, delete
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
@ -191,6 +187,7 @@ class MobileViewBottomSheetBody extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
const HSpace(8),
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_delete_m,
|
||||
@ -202,12 +199,15 @@ class MobileViewBottomSheetBody extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
const VSpace(8),
|
||||
|
||||
// favorites
|
||||
BottomSheetActionWidget(
|
||||
svg: isFavorite
|
||||
? FlowySvgs.m_favorite_selected_lg
|
||||
: FlowySvgs.m_favorite_unselected_lg,
|
||||
//TODO(yijing): switch to theme color
|
||||
iconColor: isFavorite ? Colors.yellow : null,
|
||||
text: isFavorite
|
||||
? LocaleKeys.button_removeFromFavorites.tr()
|
||||
: LocaleKeys.button_addToFavorites.tr(),
|
||||
@ -217,6 +217,7 @@ class MobileViewBottomSheetBody extends StatelessWidget {
|
||||
: MobileViewBottomSheetBodyAction.addToFavorites,
|
||||
),
|
||||
),
|
||||
const VSpace(8),
|
||||
|
||||
// help center
|
||||
BottomSheetActionWidget(
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart';
|
||||
import 'package:appflowy/mobile/presentation/page_item/mobile_slide_action_button.dart';
|
||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||
|
@ -1,7 +1,4 @@
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_drag_handler.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_rename_widget.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_view_item_body.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_view_item_header.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
@ -10,6 +7,19 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
Future<void> showMobileBottomSheet({
|
||||
required BuildContext context,
|
||||
required WidgetBuilder builder,
|
||||
}) async {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
enableDrag: true,
|
||||
useSafeArea: true,
|
||||
builder: builder,
|
||||
);
|
||||
}
|
||||
|
||||
enum MobileBottomSheetType {
|
||||
view,
|
||||
rename,
|
||||
@ -42,21 +52,18 @@ class _MobileViewItemBottomSheetState extends State<MobileViewItemBottomSheet> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// drag handler
|
||||
const MobileBottomSheetDragHandler(),
|
||||
|
||||
// header
|
||||
_buildHeader(),
|
||||
const VSpace(8.0),
|
||||
const Divider(),
|
||||
|
||||
// body
|
||||
_buildBody(),
|
||||
const VSpace(24.0),
|
||||
],
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 32),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// header
|
||||
_buildHeader(),
|
||||
const VSpace(16),
|
||||
// body
|
||||
_buildBody(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -90,23 +97,24 @@ class _MobileViewItemBottomSheetState extends State<MobileViewItemBottomSheet> {
|
||||
});
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.duplicate:
|
||||
context.read<ViewBloc>().add(const ViewEvent.duplicate());
|
||||
context.pop();
|
||||
context.read<ViewBloc>().add(const ViewEvent.duplicate());
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.share:
|
||||
// unimplemented
|
||||
context.pop();
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.delete:
|
||||
context.read<ViewBloc>().add(const ViewEvent.delete());
|
||||
context.pop();
|
||||
context.read<ViewBloc>().add(const ViewEvent.delete());
|
||||
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.addToFavorites:
|
||||
case MobileViewItemBottomSheetBodyAction.removeFromFavorites:
|
||||
context.pop();
|
||||
context
|
||||
.read<FavoriteBloc>()
|
||||
.add(FavoriteEvent.toggle(widget.view));
|
||||
context.pop();
|
||||
break;
|
||||
}
|
||||
},
|
@ -1,49 +0,0 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MobileErrorPage extends StatelessWidget {
|
||||
const MobileErrorPage({
|
||||
super.key,
|
||||
this.header,
|
||||
this.title,
|
||||
required this.message,
|
||||
});
|
||||
|
||||
final Widget? header;
|
||||
final String? title;
|
||||
final String message;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
header != null
|
||||
? header!
|
||||
: const FlowyText.semibold(
|
||||
'😔',
|
||||
fontSize: 50,
|
||||
),
|
||||
const VSpace(14.0),
|
||||
FlowyText.semibold(
|
||||
title ?? LocaleKeys.error_weAreSorry.tr(),
|
||||
fontSize: 32,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const VSpace(4.0),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: FlowyText.regular(
|
||||
message,
|
||||
fontSize: 16,
|
||||
maxLines: 100,
|
||||
color: Colors.grey, // FIXME: use theme color
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||
import 'package:appflowy/mobile/presentation/error/error_page.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/favorite_folder/mobile_home_favorite_folder.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_state_container.dart';
|
||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/menu/menu_bloc.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
@ -49,27 +49,27 @@ class MobileFavoritePageFolder extends StatelessWidget {
|
||||
builder: (context) {
|
||||
final favoriteState = context.watch<FavoriteBloc>().state;
|
||||
if (favoriteState.views.isEmpty) {
|
||||
return MobileErrorPage(
|
||||
header: const FlowyText.semibold(
|
||||
'😁',
|
||||
fontSize: 50,
|
||||
),
|
||||
return FlowyMobileStateContainer.info(
|
||||
emoji: '😁',
|
||||
title: LocaleKeys.favorite_noFavorite.tr(),
|
||||
message: LocaleKeys.favorite_noFavoriteHintText.tr(),
|
||||
description: LocaleKeys.favorite_noFavoriteHintText.tr(),
|
||||
);
|
||||
}
|
||||
return Scrollbar(
|
||||
child: SingleChildScrollView(
|
||||
child: SlidableAutoCloseBehavior(
|
||||
child: Column(
|
||||
children: [
|
||||
MobileFavoriteFolder(
|
||||
showHeader: false,
|
||||
forceExpanded: true,
|
||||
views: favoriteState.views,
|
||||
),
|
||||
const VSpace(100.0),
|
||||
],
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: SlidableAutoCloseBehavior(
|
||||
child: Column(
|
||||
children: [
|
||||
MobileFavoriteFolder(
|
||||
showHeader: false,
|
||||
forceExpanded: true,
|
||||
views: favoriteState.views,
|
||||
),
|
||||
const VSpace(100.0),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -82,12 +82,9 @@ class MobileFavoritePage extends StatelessWidget {
|
||||
|
||||
// Folder
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: MobileFavoritePageFolder(
|
||||
userProfile: userProfile,
|
||||
workspaceSetting: workspaceSetting,
|
||||
),
|
||||
child: MobileFavoritePageFolder(
|
||||
userProfile: userProfile,
|
||||
workspaceSetting: workspaceSetting,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -110,7 +110,10 @@ class MobileHomePage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const _TrashButton(),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: _TrashButton(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -142,7 +145,10 @@ class _TrashButton extends StatelessWidget {
|
||||
LocaleKeys.trash_text.tr(),
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
),
|
||||
style: const ButtonStyle(alignment: Alignment.centerLeft),
|
||||
style: const ButtonStyle(
|
||||
alignment: Alignment.centerLeft,
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ class MobileHomePageRecentFilesWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
// TODO: implement the details later.
|
||||
return SizedBox(
|
||||
height: 168,
|
||||
@ -54,13 +55,10 @@ class MobileHomePageRecentFilesWidget extends StatelessWidget {
|
||||
return Container(
|
||||
width: 120,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: theme.colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.outline
|
||||
.withOpacity(0.5),
|
||||
color: theme.colorScheme.outline.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
child: Stack(
|
||||
@ -104,14 +102,9 @@ class MobileHomePageRecentFilesWidget extends StatelessWidget {
|
||||
child: Text(
|
||||
recentFilesList[index].title,
|
||||
softWrap: true,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onBackground,
|
||||
),
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: theme.colorScheme.onBackground,
|
||||
),
|
||||
maxLines: 2,
|
||||
),
|
||||
),
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/presentation.dart';
|
||||
|
||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
@ -23,33 +23,47 @@ class _MobileHomeSettingPageState extends State<MobileHomeSettingPage> {
|
||||
return FutureBuilder(
|
||||
future: getIt<AuthService>().getUser(),
|
||||
builder: ((context, snapshot) {
|
||||
String? errorMsg;
|
||||
if (!snapshot.hasData) {
|
||||
return const Center(child: CircularProgressIndicator.adaptive());
|
||||
}
|
||||
final userProfile = snapshot.data?.fold((error) => null, (userProfile) {
|
||||
final userProfile = snapshot.data?.fold((error) {
|
||||
errorMsg = error.msg;
|
||||
return null;
|
||||
}, (userProfile) {
|
||||
return userProfile;
|
||||
});
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(LocaleKeys.settings_title.tr()),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
children: [
|
||||
PersonalInfoSettingGroup(
|
||||
userProfile: userProfile,
|
||||
body: userProfile == null
|
||||
? FlowyMobileStateContainer.error(
|
||||
emoji: '🛸',
|
||||
title: LocaleKeys.settings_mobile_userprofileError.tr(),
|
||||
description: LocaleKeys
|
||||
.settings_mobile_userprofileErrorDescription
|
||||
.tr(),
|
||||
errorMsg: errorMsg,
|
||||
)
|
||||
: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
children: [
|
||||
PersonalInfoSettingGroup(
|
||||
userProfile: userProfile,
|
||||
),
|
||||
// TODO(yijing): implement this along with Notification Page
|
||||
const NotificationsSettingGroup(),
|
||||
const AppearanceSettingGroup(),
|
||||
const SupportSettingGroup(),
|
||||
const AboutSettingGroup(),
|
||||
],
|
||||
),
|
||||
),
|
||||
// TODO(yijing): implement this along with Notification Page
|
||||
const NotificationsSettingGroup(),
|
||||
const AppearanceSettingGroup(),
|
||||
const SupportSettingGroup(),
|
||||
const AboutSettingGroup(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
@ -1,12 +1,13 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_action_widget.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/show_flowy_mobile_bottom_sheet.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||
import 'package:appflowy/plugins/trash/application/prelude.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class MobileHomeTrashPage extends StatelessWidget {
|
||||
@ -18,61 +19,53 @@ class MobileHomeTrashPage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<TrashBloc>()..add(const TrashEvent.initial()),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
child: BlocBuilder<TrashBloc, TrashState>(
|
||||
builder: (context, state) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(LocaleKeys.trash_text.tr()),
|
||||
elevation: 0,
|
||||
actions: [
|
||||
IconButton(
|
||||
splashRadius: 20,
|
||||
icon: const Icon(Icons.more_horiz),
|
||||
onPressed: () {
|
||||
showFlowyMobileBottomSheet(
|
||||
context,
|
||||
title: LocaleKeys.trash_mobile_actions.tr(),
|
||||
builder: (_) => Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_restore_m,
|
||||
text: LocaleKeys.trash_restoreAll.tr(),
|
||||
onTap: () {
|
||||
context
|
||||
..read<TrashBloc>()
|
||||
.add(const TrashEvent.restoreAll())
|
||||
..pop();
|
||||
},
|
||||
state.objects.isEmpty
|
||||
? const SizedBox.shrink()
|
||||
: IconButton(
|
||||
splashRadius: 20,
|
||||
icon: const Icon(Icons.more_horiz),
|
||||
onPressed: () {
|
||||
final trashBloc = context.read<TrashBloc>();
|
||||
showFlowyMobileBottomSheet(
|
||||
context,
|
||||
title: LocaleKeys.trash_mobile_actions.tr(),
|
||||
builder: (_) => Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _TrashActionAllButton(
|
||||
trashBloc: trashBloc,
|
||||
type: _TrashActionType.deleteAll,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: _TrashActionAllButton(
|
||||
trashBloc: trashBloc,
|
||||
type: _TrashActionType.restoreAll,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_delete_m,
|
||||
text: LocaleKeys.trash_deleteAll.tr(),
|
||||
onTap: () {
|
||||
context
|
||||
..read<TrashBloc>()
|
||||
.add(const TrashEvent.deleteAll())
|
||||
..pop();
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: BlocBuilder<TrashBloc, TrashState>(
|
||||
builder: (_, state) {
|
||||
if (state.objects.isEmpty) {
|
||||
return const _TrashEmptyPage();
|
||||
}
|
||||
return _DeletedFilesListView(state);
|
||||
},
|
||||
),
|
||||
body: state.objects.isEmpty
|
||||
? FlowyMobileStateContainer.info(
|
||||
emoji: '🗑️',
|
||||
title: LocaleKeys.trash_mobile_empty.tr(),
|
||||
description: LocaleKeys.trash_mobile_emptyDescription.tr(),
|
||||
)
|
||||
: _DeletedFilesListView(state),
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -80,6 +73,76 @@ class MobileHomeTrashPage extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
enum _TrashActionType {
|
||||
restoreAll,
|
||||
deleteAll,
|
||||
}
|
||||
|
||||
class _TrashActionAllButton extends StatelessWidget {
|
||||
/// Switch between 'delete all' and 'restore all' feature
|
||||
const _TrashActionAllButton({
|
||||
this.type = _TrashActionType.deleteAll,
|
||||
required this.trashBloc,
|
||||
});
|
||||
final _TrashActionType type;
|
||||
final TrashBloc trashBloc;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final isDeleteAll = type == _TrashActionType.deleteAll;
|
||||
return BlocProvider.value(
|
||||
value: trashBloc,
|
||||
child: BottomSheetActionWidget(
|
||||
svg: isDeleteAll ? FlowySvgs.m_delete_m : FlowySvgs.m_restore_m,
|
||||
text: isDeleteAll
|
||||
? LocaleKeys.trash_deleteAll.tr()
|
||||
: LocaleKeys.trash_restoreAll.tr(),
|
||||
onTap: () {
|
||||
final trashList = trashBloc.state.objects;
|
||||
if (trashList.isNotEmpty) {
|
||||
context.pop();
|
||||
showFlowyMobileConfirmDialog(
|
||||
context,
|
||||
title: isDeleteAll
|
||||
? LocaleKeys.trash_confirmDeleteAll_title.tr()
|
||||
: LocaleKeys.trash_restoreAll.tr(),
|
||||
content: isDeleteAll
|
||||
? LocaleKeys.trash_confirmDeleteAll_caption.tr()
|
||||
: LocaleKeys.trash_confirmRestoreAll_caption.tr(),
|
||||
actionButtonTitle: isDeleteAll
|
||||
? LocaleKeys.trash_deleteAll.tr()
|
||||
: LocaleKeys.trash_restoreAll.tr(),
|
||||
actionButtonColor: isDeleteAll
|
||||
? theme.colorScheme.error
|
||||
: theme.colorScheme.primary,
|
||||
onActionButtonPressed: () {
|
||||
if (isDeleteAll) {
|
||||
trashBloc.add(
|
||||
const TrashEvent.deleteAll(),
|
||||
);
|
||||
} else {
|
||||
trashBloc.add(
|
||||
const TrashEvent.restoreAll(),
|
||||
);
|
||||
}
|
||||
},
|
||||
cancelButtonTitle: LocaleKeys.button_cancel.tr(),
|
||||
);
|
||||
} else {
|
||||
// when there is no deleted files
|
||||
// show toast
|
||||
Fluttertoast.showToast(
|
||||
msg: LocaleKeys.trash_mobile_empty.tr(),
|
||||
gravity: ToastGravity.CENTER,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DeletedFilesListView extends StatelessWidget {
|
||||
const _DeletedFilesListView(
|
||||
this.state,
|
||||
@ -89,95 +152,75 @@ class _DeletedFilesListView extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return ListView.builder(
|
||||
itemBuilder: (context, index) {
|
||||
final object = state.objects[index];
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
child: ListTile(
|
||||
// TODO(Yijing): implement file type after TrashPB has file type
|
||||
leading: FlowySvg(
|
||||
FlowySvgs.documents_s,
|
||||
size: const Size.square(24),
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
title: Text(
|
||||
object.name,
|
||||
style: theme.textTheme.labelMedium
|
||||
?.copyWith(color: theme.colorScheme.onBackground),
|
||||
),
|
||||
horizontalTitleGap: 0,
|
||||
// TODO(yiing): needs improve by container/surface theme color
|
||||
tileColor: theme.colorScheme.onSurface.withOpacity(0.1),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// TODO(yijing): extract icon button
|
||||
IconButton(
|
||||
splashRadius: 20,
|
||||
icon: FlowySvg(
|
||||
FlowySvgs.m_restore_m,
|
||||
size: const Size.square(24),
|
||||
color: theme.colorScheme.onSurface,
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: ListView.builder(
|
||||
itemBuilder: (context, index) {
|
||||
final object = state.objects[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
child: ListTile(
|
||||
// TODO(Yijing): implement file type after TrashPB has file type
|
||||
leading: FlowySvg(
|
||||
FlowySvgs.documents_s,
|
||||
size: const Size.square(24),
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
title: Text(
|
||||
object.name,
|
||||
style: theme.textTheme.labelMedium
|
||||
?.copyWith(color: theme.colorScheme.onBackground),
|
||||
),
|
||||
horizontalTitleGap: 0,
|
||||
// TODO(yiing): needs improve by container/surface theme color
|
||||
tileColor: theme.colorScheme.onSurface.withOpacity(0.1),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// TODO(yijing): extract icon button
|
||||
IconButton(
|
||||
splashRadius: 20,
|
||||
icon: FlowySvg(
|
||||
FlowySvgs.m_restore_m,
|
||||
size: const Size.square(24),
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
onPressed: () {
|
||||
context
|
||||
.read<TrashBloc>()
|
||||
.add(TrashEvent.putback(object.id));
|
||||
Fluttertoast.showToast(
|
||||
msg:
|
||||
'${object.name} ${LocaleKeys.trash_mobile_isRestored.tr()}',
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
},
|
||||
),
|
||||
onPressed: () {
|
||||
context
|
||||
.read<TrashBloc>()
|
||||
.add(TrashEvent.putback(object.id));
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
splashRadius: 20,
|
||||
icon: FlowySvg(
|
||||
FlowySvgs.m_delete_m,
|
||||
size: const Size.square(24),
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
onPressed: () {
|
||||
context.read<TrashBloc>().add(TrashEvent.delete(object));
|
||||
},
|
||||
)
|
||||
],
|
||||
IconButton(
|
||||
splashRadius: 20,
|
||||
icon: FlowySvg(
|
||||
FlowySvgs.m_delete_m,
|
||||
size: const Size.square(24),
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
onPressed: () {
|
||||
context.read<TrashBloc>().add(TrashEvent.delete(object));
|
||||
Fluttertoast.showToast(
|
||||
msg:
|
||||
'${object.name} ${LocaleKeys.trash_mobile_isDeleted.tr()}',
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: state.objects.length,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TrashEmptyPage extends StatelessWidget {
|
||||
const _TrashEmptyPage();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Text(
|
||||
'🗑️',
|
||||
style: TextStyle(fontSize: 40),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
LocaleKeys.trash_mobile_empty.tr(),
|
||||
style: theme.textTheme.labelLarge,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
LocaleKeys.trash_mobile_emptyDescription.tr(),
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: theme.hintColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
itemCount: state.objects.length,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_add_new_page.dart';
|
||||
import 'package:appflowy/mobile/presentation/page_item/mobile_view_item_add_button.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
||||
@ -395,20 +395,24 @@ class _SingleMobileInnerViewItemState extends State<SingleMobileInnerViewItem> {
|
||||
Widget _buildViewAddButton(BuildContext context) {
|
||||
return MobileViewAddButton(
|
||||
onPressed: () {
|
||||
showMobileBottomSheet(
|
||||
context: context,
|
||||
builder: (_) => AddNewPageWidgetBottomSheet(
|
||||
view: widget.view,
|
||||
onAction: (layout) {
|
||||
context.pop();
|
||||
context.read<ViewBloc>().add(
|
||||
ViewEvent.createView(
|
||||
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
layout,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
final title = widget.view.name;
|
||||
showFlowyMobileBottomSheet(
|
||||
context,
|
||||
title: title,
|
||||
builder: (_) {
|
||||
return AddNewPageWidgetBottomSheet(
|
||||
view: widget.view,
|
||||
onAction: (layout) {
|
||||
context.pop();
|
||||
context.read<ViewBloc>().add(
|
||||
ViewEvent.createView(
|
||||
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
layout,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -16,7 +16,7 @@ class PersonalInfoSettingGroup extends StatelessWidget {
|
||||
required this.userProfile,
|
||||
});
|
||||
|
||||
final UserProfilePB? userProfile;
|
||||
final UserProfilePB userProfile;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -33,9 +33,9 @@ class PersonalInfoSettingGroup extends StatelessWidget {
|
||||
settingItemList: [
|
||||
MobileSettingItem(
|
||||
name: userName,
|
||||
subtitle: isCloudEnabled && userProfile != null
|
||||
subtitle: isCloudEnabled
|
||||
? Text(
|
||||
userProfile!.email,
|
||||
userProfile.email,
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
|
@ -4,6 +4,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
import 'widgets/widgets.dart';
|
||||
|
||||
@ -14,32 +15,33 @@ class SupportSettingGroup extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MobileSettingGroup(
|
||||
groupTitle: LocaleKeys.settings_mobile_support.tr(),
|
||||
settingItemList: [
|
||||
// 'Help Center'
|
||||
MobileSettingItem(
|
||||
name: LocaleKeys.settings_mobile_joinDiscord.tr(),
|
||||
trailing: const Icon(
|
||||
Icons.chevron_right,
|
||||
return FutureBuilder(
|
||||
future: PackageInfo.fromPlatform(),
|
||||
builder: (context, snapshot) => MobileSettingGroup(
|
||||
groupTitle: LocaleKeys.settings_mobile_support.tr(),
|
||||
settingItemList: [
|
||||
MobileSettingItem(
|
||||
name: LocaleKeys.settings_mobile_joinDiscord.tr(),
|
||||
trailing: const Icon(
|
||||
Icons.chevron_right,
|
||||
),
|
||||
onTap: () => safeLaunchUrl('https://discord.gg/JucBXeU2FE'),
|
||||
),
|
||||
onTap: () => safeLaunchUrl('https://discord.gg/JucBXeU2FE'),
|
||||
),
|
||||
MobileSettingItem(
|
||||
name: LocaleKeys.workspace_errorActions_reportIssue.tr(),
|
||||
trailing: const Icon(
|
||||
Icons.chevron_right,
|
||||
MobileSettingItem(
|
||||
name: LocaleKeys.workspace_errorActions_reportIssue.tr(),
|
||||
trailing: const Icon(
|
||||
Icons.chevron_right,
|
||||
),
|
||||
onTap: () {
|
||||
final String? version = snapshot.data?.version;
|
||||
final String os = Platform.operatingSystem;
|
||||
safeLaunchUrl(
|
||||
'https://github.com/AppFlowy-IO/AppFlowy/issues/new?assignees=&labels=&projects=&template=bug_report.yaml&title=[Bug]%20Mobile:%20&version=$version&os=$os',
|
||||
);
|
||||
},
|
||||
),
|
||||
onTap: () {
|
||||
// TODO(yijing): get app version before release
|
||||
const String version = 'Beta';
|
||||
final String os = Platform.operatingSystem;
|
||||
safeLaunchUrl(
|
||||
'https://github.com/AppFlowy-IO/AppFlowy/issues/new?assignees=&labels=&projects=&template=bug_report.yaml&title=[Bug]%20Mobile:%20&version=$version&os=$os',
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,102 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
enum _FlowyMobileStateContainerType {
|
||||
info,
|
||||
error,
|
||||
}
|
||||
|
||||
/// Used to display info(like empty state) or error state
|
||||
/// error state has two buttons to report issue with error message or reach out on discord
|
||||
class FlowyMobileStateContainer extends StatelessWidget {
|
||||
const FlowyMobileStateContainer.error({
|
||||
this.emoji,
|
||||
required this.title,
|
||||
this.description,
|
||||
required this.errorMsg,
|
||||
super.key,
|
||||
}) : _stateType = _FlowyMobileStateContainerType.error;
|
||||
|
||||
const FlowyMobileStateContainer.info({
|
||||
this.emoji,
|
||||
required this.title,
|
||||
this.description,
|
||||
super.key,
|
||||
}) : errorMsg = null,
|
||||
_stateType = _FlowyMobileStateContainerType.info;
|
||||
|
||||
final String? emoji;
|
||||
final String title;
|
||||
final String? description;
|
||||
final String? errorMsg;
|
||||
final _FlowyMobileStateContainerType _stateType;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return SizedBox.expand(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 32),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
emoji ?? '',
|
||||
style: const TextStyle(fontSize: 40),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
title,
|
||||
style: theme.textTheme.labelLarge,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
description ?? '',
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: theme.hintColor,
|
||||
),
|
||||
),
|
||||
if (_stateType == _FlowyMobileStateContainerType.error) ...[
|
||||
const SizedBox(height: 8),
|
||||
FutureBuilder(
|
||||
future: PackageInfo.fromPlatform(),
|
||||
builder: (context, snapshot) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
OutlinedButton(
|
||||
onPressed: () {
|
||||
final String? version = snapshot.data?.version;
|
||||
final String os = Platform.operatingSystem;
|
||||
safeLaunchUrl(
|
||||
'https://github.com/AppFlowy-IO/AppFlowy/issues/new?assignees=&labels=&projects=&template=bug_report.yaml&title=[Bug]%20Mobile:%20&version=$version&os=$os&context=Error%20log:%20$errorMsg',
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
LocaleKeys.workspace_errorActions_reportIssue.tr(),
|
||||
),
|
||||
),
|
||||
OutlinedButton(
|
||||
onPressed: () =>
|
||||
safeLaunchUrl('https://discord.gg/JucBXeU2FE'),
|
||||
child: Text(
|
||||
LocaleKeys.workspace_errorActions_reachOut.tr(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
)
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
///show the dialog to confirm one single action
|
||||
///[onActionButtonPressed] and [onCancelButtonPressed] end with close the dialog
|
||||
Future<T?> showFlowyMobileConfirmDialog<T>(
|
||||
BuildContext context, {
|
||||
String? title,
|
||||
String? content,
|
||||
required String actionButtonTitle,
|
||||
Color? actionButtonColor,
|
||||
String? cancelButtonTitle,
|
||||
required void Function()? onActionButtonPressed,
|
||||
void Function()? onCancelButtonPressed,
|
||||
}) async {
|
||||
return showDialog(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
final foregroundColor = Theme.of(context).colorScheme.onSurface;
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
title ?? "",
|
||||
),
|
||||
content: Text(
|
||||
content ?? "",
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text(
|
||||
actionButtonTitle,
|
||||
style: TextStyle(
|
||||
color: actionButtonColor ?? foregroundColor,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
onActionButtonPressed?.call();
|
||||
// we cannot use dialogContext.pop() here because this is no GoRouter in dialogContext. Use Navigator instead to close the dialog.
|
||||
Navigator.of(
|
||||
dialogContext,
|
||||
).pop();
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: Text(
|
||||
cancelButtonTitle ?? LocaleKeys.button_cancel.tr(),
|
||||
style: TextStyle(
|
||||
color: foregroundColor,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
onCancelButtonPressed?.call();
|
||||
Navigator.of(
|
||||
dialogContext,
|
||||
).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
export 'show_flowy_mobile_confirm_dialog.dart';
|
||||
export 'show_flowy_mobile_bottom_sheet.dart';
|
||||
export 'flowy_mobile_state_container.dart';
|
@ -120,7 +120,9 @@
|
||||
"mobile": {
|
||||
"actions": "Trash Actions",
|
||||
"empty": "Trash Bin is Empty",
|
||||
"emptyDescription": "You don't have any deleted file"
|
||||
"emptyDescription": "You don't have any deleted file",
|
||||
"isDeleted": "is deleted",
|
||||
"isRestored": "is restored"
|
||||
}
|
||||
},
|
||||
"deletePagePrompt": {
|
||||
@ -394,7 +396,9 @@
|
||||
"support": "Support",
|
||||
"joinDiscord": "Join us in Discord",
|
||||
"privacyPolicy": "Privacy Policy",
|
||||
"userAgreement": "User Agreement"
|
||||
"userAgreement": "User Agreement",
|
||||
"userprofileError": "Failed to load user profile",
|
||||
"userprofileErrorDescription": "Please try to log out and log back in to check if the issue still persists."
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
|
Loading…
Reference in New Issue
Block a user