mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: editor page issues (#4055)
* fix: disable editor in card detail page * fix: mobile toolbar disappears after editing link * fix: favorite icon shows incorrectly * fix: inkWell when pressing on the Trash is different from the rest of our list tiles in the app * fix: recent view didn't update after deleting view * fix: trash button too small * feat: use new bottom sheet style in cover plugin * feat: use new bottom sheet style in add new page * feat: use new bottom sheet style in view page option * feat: use new bottom sheet style in image block * feat: use new bottom sheet style in settings block and edit link menu * fix: data picker doesn't show * fix: flutter analyze
This commit is contained in:
@ -2,28 +2,21 @@ import 'package:appflowy/mobile/presentation/database/board/mobile_board_screen.
|
|||||||
import 'package:appflowy/mobile/presentation/database/mobile_calendar_screen.dart';
|
import 'package:appflowy/mobile/presentation/database/mobile_calendar_screen.dart';
|
||||||
import 'package:appflowy/mobile/presentation/database/mobile_grid_screen.dart';
|
import 'package:appflowy/mobile/presentation/database/mobile_grid_screen.dart';
|
||||||
import 'package:appflowy/mobile/presentation/presentation.dart';
|
import 'package:appflowy/mobile/presentation/presentation.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/workspace/application/recent/recent_service.dart';
|
||||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
class MobileRouterRecord {
|
|
||||||
PropertyValueNotifier<String> lastPushedRouter =
|
|
||||||
PropertyValueNotifier<String>('');
|
|
||||||
}
|
|
||||||
|
|
||||||
extension MobileRouter on BuildContext {
|
extension MobileRouter on BuildContext {
|
||||||
Future<void> pushView(ViewPB view) async {
|
Future<void> pushView(ViewPB view) async {
|
||||||
await FolderEventSetLatestView(ViewIdPB(value: view.id)).send();
|
|
||||||
getIt<MobileRouterRecord>().lastPushedRouter.value = view.routeName;
|
|
||||||
push(
|
push(
|
||||||
Uri(
|
Uri(
|
||||||
path: view.routeName,
|
path: view.routeName,
|
||||||
queryParameters: view.queryParameters,
|
queryParameters: view.queryParameters,
|
||||||
).toString(),
|
).toString(),
|
||||||
);
|
).then((value) {
|
||||||
|
RecentService().updateRecentViews([view.id], true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +149,8 @@ class _MobileViewPageState extends State<MobileViewPage> {
|
|||||||
return AppBarMoreButton(
|
return AppBarMoreButton(
|
||||||
onTap: (context) {
|
onTap: (context) {
|
||||||
showMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context: context,
|
context,
|
||||||
|
showDragHandle: true,
|
||||||
builder: (_) => _buildViewPageBottomSheet(context),
|
builder: (_) => _buildViewPageBottomSheet(context),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -2,6 +2,7 @@ export 'bottom_sheet_action_widget.dart';
|
|||||||
export 'bottom_sheet_add_new_page.dart';
|
export 'bottom_sheet_add_new_page.dart';
|
||||||
export 'bottom_sheet_drag_handler.dart';
|
export 'bottom_sheet_drag_handler.dart';
|
||||||
export 'bottom_sheet_rename_widget.dart';
|
export 'bottom_sheet_rename_widget.dart';
|
||||||
|
export 'bottom_sheet_view_item.dart';
|
||||||
export 'bottom_sheet_view_item_body.dart';
|
export 'bottom_sheet_view_item_body.dart';
|
||||||
export 'bottom_sheet_view_item_header.dart';
|
export 'bottom_sheet_view_item_header.dart';
|
||||||
export 'bottom_sheet_view_page.dart';
|
export 'bottom_sheet_view_page.dart';
|
||||||
|
@ -52,12 +52,7 @@ class _MobileBottomSheetEditLinkWidgetState
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Padding(
|
return Column(
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 4.0,
|
|
||||||
vertical: 16.0,
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
_buildTextField(textController, null),
|
_buildTextField(textController, null),
|
||||||
@ -95,7 +90,6 @@ class _MobileBottomSheetEditLinkWidgetState
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,118 @@
|
|||||||
|
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';
|
||||||
|
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';
|
||||||
|
|
||||||
|
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: [
|
||||||
|
// header
|
||||||
|
_buildHeader(),
|
||||||
|
const VSpace(16),
|
||||||
|
// body
|
||||||
|
_buildBody(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
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:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ class MobileViewItemBottomSheetHeader extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
return Row(
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
// back button,
|
// back button,
|
||||||
showBackButton
|
showBackButton
|
||||||
@ -31,23 +33,22 @@ class MobileViewItemBottomSheetHeader extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: const SizedBox.shrink(),
|
: FlowyButton(
|
||||||
// title
|
useIntrinsicWidth: true,
|
||||||
Expanded(
|
text: const Icon(
|
||||||
child: Text(
|
|
||||||
view.name,
|
|
||||||
style: theme.textTheme.labelSmall,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
Icons.close,
|
Icons.close,
|
||||||
color: theme.hintColor,
|
|
||||||
),
|
),
|
||||||
onPressed: () {
|
margin: EdgeInsets.zero,
|
||||||
|
onTap: () {
|
||||||
context.pop();
|
context.pop();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
// title
|
||||||
|
Text(
|
||||||
|
view.name,
|
||||||
|
style: theme.textTheme.labelSmall,
|
||||||
|
),
|
||||||
|
const HSpace(24.0),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -117,33 +117,6 @@ class MobileViewBottomSheetBody extends StatelessWidget {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
// undo, redo
|
|
||||||
// Row(
|
|
||||||
// mainAxisSize: MainAxisSize.max,
|
|
||||||
// children: [
|
|
||||||
// Expanded(
|
|
||||||
// child: BottomSheetActionWidget(
|
|
||||||
// svg: FlowySvgs.m_undo_m,
|
|
||||||
// text: LocaleKeys.toolbar_undo.tr(),
|
|
||||||
// onTap: () => onAction(
|
|
||||||
// MobileViewBottomSheetBodyAction.undo,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// const HSpace(8),
|
|
||||||
// Expanded(
|
|
||||||
// child: BottomSheetActionWidget(
|
|
||||||
// svg: FlowySvgs.m_redo_m,
|
|
||||||
// text: LocaleKeys.toolbar_redo.tr(),
|
|
||||||
// onTap: () => onAction(
|
|
||||||
// MobileViewBottomSheetBodyAction.redo,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// const VSpace(8),
|
|
||||||
|
|
||||||
// rename, duplicate
|
// rename, duplicate
|
||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart';
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||||
import 'package:appflowy/mobile/presentation/page_item/mobile_slide_action_button.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/favorite/favorite_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
@ -51,7 +51,8 @@ enum MobilePaneActionType {
|
|||||||
final viewBloc = context.read<ViewBloc>();
|
final viewBloc = context.read<ViewBloc>();
|
||||||
final favoriteBloc = context.read<FavoriteBloc>();
|
final favoriteBloc = context.read<FavoriteBloc>();
|
||||||
showMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context: context,
|
context,
|
||||||
|
showDragHandle: true,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return MultiBlocProvider(
|
return MultiBlocProvider(
|
||||||
providers: [
|
providers: [
|
||||||
|
@ -1,22 +1,26 @@
|
|||||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
import 'package:appflowy/plugins/base/drag_handler.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/size.dart';
|
import 'package:flowy_infra/size.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart' hide WidgetBuilder;
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart' hide WidgetBuilder;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
Future<void> showMobileBottomSheet({
|
Future<T?> showMobileBottomSheet<T>(
|
||||||
required BuildContext context,
|
BuildContext context, {
|
||||||
required WidgetBuilder builder,
|
required WidgetBuilder builder,
|
||||||
bool isDragEnabled = true,
|
|
||||||
ShapeBorder? shape,
|
ShapeBorder? shape,
|
||||||
|
bool isDragEnabled = true,
|
||||||
bool resizeToAvoidBottomInset = true,
|
bool resizeToAvoidBottomInset = true,
|
||||||
EdgeInsets padding = const EdgeInsets.fromLTRB(16, 16, 16, 32),
|
EdgeInsets padding = const EdgeInsets.fromLTRB(16, 16, 16, 32),
|
||||||
|
bool showDragHandle = false,
|
||||||
|
bool showHeader = false,
|
||||||
|
bool showCloseButton = false,
|
||||||
|
String title = '', // only works if showHeader is true
|
||||||
}) async {
|
}) async {
|
||||||
showModalBottomSheet(
|
assert(() {
|
||||||
|
if (showCloseButton || title.isNotEmpty) assert(showHeader);
|
||||||
|
return true;
|
||||||
|
}());
|
||||||
|
|
||||||
|
return showModalBottomSheet<T>(
|
||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
enableDrag: isDragEnabled,
|
enableDrag: isDragEnabled,
|
||||||
@ -29,128 +33,74 @@ Future<void> showMobileBottomSheet({
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
|
final List<Widget> children = [];
|
||||||
|
|
||||||
|
if (showDragHandle) {
|
||||||
|
children.addAll([
|
||||||
|
const VSpace(4),
|
||||||
|
const DragHandler(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showHeader) {
|
||||||
|
children.addAll([
|
||||||
|
const VSpace(4),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
showCloseButton
|
||||||
|
? Padding(
|
||||||
|
padding: EdgeInsets.only(left: padding.left),
|
||||||
|
child: FlowyButton(
|
||||||
|
useIntrinsicWidth: true,
|
||||||
|
text: const Icon(
|
||||||
|
Icons.close,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
onTap: () => Navigator.of(context).pop(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
FlowyText(
|
||||||
|
title,
|
||||||
|
fontSize: 16.0,
|
||||||
|
),
|
||||||
|
showCloseButton
|
||||||
|
? HSpace(padding.right + 24)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const VSpace(4),
|
||||||
|
const Divider(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
final child = builder(context);
|
final child = builder(context);
|
||||||
|
|
||||||
if (resizeToAvoidBottomInset) {
|
if (resizeToAvoidBottomInset) {
|
||||||
return AnimatedPadding(
|
children.add(
|
||||||
|
AnimatedPadding(
|
||||||
padding: padding +
|
padding: padding +
|
||||||
EdgeInsets.only(
|
EdgeInsets.only(
|
||||||
bottom: MediaQuery.of(context).viewInsets.bottom,
|
bottom: MediaQuery.of(context).viewInsets.bottom,
|
||||||
),
|
),
|
||||||
duration: Duration.zero,
|
duration: Duration.zero,
|
||||||
child: child,
|
child: child,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
return child;
|
children.add(child);
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MobileBottomSheetType {
|
if (children.length == 1) {
|
||||||
view,
|
return children.first;
|
||||||
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(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: children,
|
||||||
// header
|
|
||||||
_buildHeader(),
|
|
||||||
const VSpace(16),
|
|
||||||
// body
|
|
||||||
_buildBody(),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -2,6 +2,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_action_widget.dart';
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_action_widget.dart';
|
||||||
|
import 'package:appflowy/mobile/presentation/database/card/card_detail/widgets/mobile_row_property_list.dart';
|
||||||
import 'package:appflowy/mobile/presentation/widgets/show_flowy_mobile_bottom_sheet.dart';
|
import 'package:appflowy/mobile/presentation/widgets/show_flowy_mobile_bottom_sheet.dart';
|
||||||
import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart';
|
import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart';
|
||||||
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
||||||
@ -12,8 +13,6 @@ import 'package:appflowy/plugins/database_view/grid/application/row/row_action_s
|
|||||||
import 'package:appflowy/plugins/database_view/grid/application/row/row_detail_bloc.dart';
|
import 'package:appflowy/plugins/database_view/grid/application/row/row_detail_bloc.dart';
|
||||||
import 'package:appflowy/plugins/database_view/widgets/row/cell_builder.dart';
|
import 'package:appflowy/plugins/database_view/widgets/row/cell_builder.dart';
|
||||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/cells.dart';
|
import 'package:appflowy/plugins/database_view/widgets/row/cells/cells.dart';
|
||||||
import 'package:appflowy/mobile/presentation/database/card/card_detail/widgets/mobile_row_property_list.dart';
|
|
||||||
import 'package:appflowy/plugins/database_view/widgets/row/row_document.dart';
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
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';
|
||||||
@ -181,12 +180,6 @@ class _MobileCardDetailScreenState extends State<MobileCardDetailScreen> {
|
|||||||
fieldController: widget.fieldController,
|
fieldController: widget.fieldController,
|
||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
const VSpace(16),
|
|
||||||
RowDocument(
|
|
||||||
viewId: widget.rowController.viewId,
|
|
||||||
rowId: widget.rowController.rowId,
|
|
||||||
scrollController: widget.scrollController ?? ScrollController(),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||||
import 'package:appflowy/mobile/presentation/home/favorite_folder/mobile_home_favorite_folder.dart';
|
|
||||||
import 'package:appflowy/mobile/presentation/home/personal_folder/mobile_home_personal_folder.dart';
|
import 'package:appflowy/mobile/presentation/home/personal_folder/mobile_home_personal_folder.dart';
|
||||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/menu/menu_bloc.dart';
|
import 'package:appflowy/workspace/application/menu/menu_bloc.dart';
|
||||||
@ -48,17 +47,9 @@ class MobileFolders extends StatelessWidget {
|
|||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final menuState = context.watch<MenuBloc>().state;
|
final menuState = context.watch<MenuBloc>().state;
|
||||||
final favoriteState = context.watch<FavoriteBloc>().state;
|
|
||||||
return SlidableAutoCloseBehavior(
|
return SlidableAutoCloseBehavior(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
// TODO: Uncomment this when we have favorite folder in home page
|
|
||||||
if (showFavorite && favoriteState.views.isNotEmpty) ...[
|
|
||||||
MobileFavoriteFolder(
|
|
||||||
views: favoriteState.views,
|
|
||||||
),
|
|
||||||
const VSpace(18.0),
|
|
||||||
],
|
|
||||||
MobilePersonalFolder(
|
MobilePersonalFolder(
|
||||||
views: menuState.views,
|
views: menuState.views,
|
||||||
),
|
),
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/mobile/presentation/home/home.dart';
|
||||||
import 'package:appflowy/mobile/presentation/home/mobile_folders.dart';
|
import 'package:appflowy/mobile/presentation/home/mobile_folders.dart';
|
||||||
import 'package:appflowy/mobile/presentation/home/mobile_home_page_header.dart';
|
import 'package:appflowy/mobile/presentation/home/mobile_home_page_header.dart';
|
||||||
import 'package:appflowy/mobile/presentation/home/recent_folder/mobile_home_recent_views.dart';
|
import 'package:appflowy/mobile/presentation/home/recent_folder/mobile_home_recent_views.dart';
|
||||||
@ -10,11 +11,10 @@ import 'package:appflowy_backend/dispatch/dispatch.dart';
|
|||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/workspace.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/workspace.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
import 'home.dart';
|
|
||||||
|
|
||||||
class MobileHomeScreen extends StatelessWidget {
|
class MobileHomeScreen extends StatelessWidget {
|
||||||
const MobileHomeScreen({super.key});
|
const MobileHomeScreen({super.key});
|
||||||
|
|
||||||
@ -103,9 +103,9 @@ class MobileHomePage extends StatelessWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: MobileFolders(
|
child: MobileFolders(
|
||||||
showFavorite: false,
|
|
||||||
user: userProfile,
|
user: userProfile,
|
||||||
workspaceSetting: workspaceSetting,
|
workspaceSetting: workspaceSetting,
|
||||||
|
showFavorite: false,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
@ -129,26 +129,19 @@ class _TrashButton extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// TODO(yijing): improve style UI later
|
return FlowyButton(
|
||||||
return SizedBox(
|
expand: true,
|
||||||
width: double.infinity,
|
margin: const EdgeInsets.symmetric(vertical: 8),
|
||||||
child: TextButton.icon(
|
leftIcon: FlowySvg(
|
||||||
onPressed: () {
|
|
||||||
context.push(MobileHomeTrashPage.routeName);
|
|
||||||
},
|
|
||||||
icon: FlowySvg(
|
|
||||||
FlowySvgs.m_delete_m,
|
FlowySvgs.m_delete_m,
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
),
|
),
|
||||||
label: Text(
|
leftIconSize: const Size.square(24),
|
||||||
|
text: FlowyText(
|
||||||
LocaleKeys.trash_text.tr(),
|
LocaleKeys.trash_text.tr(),
|
||||||
style: Theme.of(context).textTheme.labelMedium,
|
fontSize: 18.0,
|
||||||
),
|
|
||||||
style: const ButtonStyle(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
splashFactory: NoSplash.splashFactory,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
onTap: () => context.push(MobileHomeTrashPage.routeName),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.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.dart';
|
||||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||||
import 'package:appflowy/plugins/trash/application/prelude.dart';
|
import 'package:appflowy/plugins/trash/application/prelude.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
@ -32,8 +32,12 @@ class MobileHomeTrashPage extends StatelessWidget {
|
|||||||
icon: const Icon(Icons.more_horiz),
|
icon: const Icon(Icons.more_horiz),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
final trashBloc = context.read<TrashBloc>();
|
final trashBloc = context.read<TrashBloc>();
|
||||||
showFlowyMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
|
showHeader: true,
|
||||||
|
showCloseButton: true,
|
||||||
|
showDragHandle: true,
|
||||||
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 32),
|
||||||
title: LocaleKeys.trash_mobile_actions.tr(),
|
title: LocaleKeys.trash_mobile_actions.tr(),
|
||||||
builder: (_) => Row(
|
builder: (_) => Row(
|
||||||
children: [
|
children: [
|
||||||
|
@ -3,6 +3,7 @@ import 'package:appflowy/mobile/presentation/bottom_sheet/default_mobile_action_
|
|||||||
import 'package:appflowy/mobile/presentation/home/personal_folder/mobile_home_personal_folder_header.dart';
|
import 'package:appflowy/mobile/presentation/home/personal_folder/mobile_home_personal_folder_header.dart';
|
||||||
import 'package:appflowy/mobile/presentation/page_item/mobile_view_item.dart';
|
import 'package:appflowy/mobile/presentation/page_item/mobile_view_item.dart';
|
||||||
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||||
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
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';
|
||||||
@ -56,13 +57,16 @@ class MobilePersonalFolder extends StatelessWidget {
|
|||||||
onSelected: (view) async {
|
onSelected: (view) async {
|
||||||
await context.pushView(view);
|
await context.pushView(view);
|
||||||
},
|
},
|
||||||
endActionPane: (context) => buildEndActionPane(context, [
|
endActionPane: (context) {
|
||||||
|
final view = context.read<ViewBloc>().state.view;
|
||||||
|
return buildEndActionPane(context, [
|
||||||
MobilePaneActionType.delete,
|
MobilePaneActionType.delete,
|
||||||
view.isFavorite
|
view.isFavorite
|
||||||
? MobilePaneActionType.removeFromFavorites
|
? MobilePaneActionType.removeFromFavorites
|
||||||
: MobilePaneActionType.addToFavorites,
|
: MobilePaneActionType.addToFavorites,
|
||||||
MobilePaneActionType.more,
|
MobilePaneActionType.more,
|
||||||
]),
|
]);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
|
||||||
import 'package:appflowy/mobile/presentation/home/recent_folder/mobile_recent_view.dart';
|
import 'package:appflowy/mobile/presentation/home/recent_folder/mobile_recent_view.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/workspace/application/recent/prelude.dart';
|
||||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||||
import 'package:dartz/dartz.dart' hide State;
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.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_infra_ui/widget/spacing.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class MobileRecentFolder extends StatefulWidget {
|
class MobileRecentFolder extends StatefulWidget {
|
||||||
const MobileRecentFolder({super.key});
|
const MobileRecentFolder({super.key});
|
||||||
@ -21,23 +18,21 @@ class MobileRecentFolder extends StatefulWidget {
|
|||||||
class _MobileRecentFolderState extends State<MobileRecentFolder> {
|
class _MobileRecentFolderState extends State<MobileRecentFolder> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ValueListenableBuilder(
|
return BlocProvider(
|
||||||
valueListenable: getIt<MobileRouterRecord>().lastPushedRouter,
|
create: (context) => RecentViewsBloc()
|
||||||
builder: (context, value, child) {
|
..add(
|
||||||
return FutureBuilder<Either<RepeatedViewPB, FlowyError>>(
|
const RecentViewsEvent.initial(),
|
||||||
future: FolderEventReadRecentViews().send(),
|
),
|
||||||
builder: (context, snapshot) {
|
child: BlocBuilder<RecentViewsBloc, RecentViewsState>(
|
||||||
final recentViews = snapshot.data
|
builder: (context, state) {
|
||||||
?.fold<List<ViewPB>>(
|
final recentViews = state
|
||||||
(l) => l.items,
|
.views
|
||||||
(r) => [],
|
|
||||||
)
|
|
||||||
// only keep the first 10 items.
|
// only keep the first 10 items.
|
||||||
.reversed
|
.reversed
|
||||||
.take(10)
|
.take(10)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
if (recentViews == null || recentViews.isEmpty) {
|
if (recentViews.isEmpty) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,8 +47,7 @@ class _MobileRecentFolderState extends State<MobileRecentFolder> {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
),
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_add_new_page.dart';
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||||
import 'package:appflowy/mobile/presentation/page_item/mobile_view_item_add_button.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/plugins/base/emoji/emoji_text.dart';
|
import 'package:appflowy/plugins/base/emoji/emoji_text.dart';
|
||||||
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.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_bloc.dart';
|
||||||
@ -397,9 +396,12 @@ class _SingleMobileInnerViewItemState extends State<SingleMobileInnerViewItem> {
|
|||||||
return MobileViewAddButton(
|
return MobileViewAddButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
final title = widget.view.name;
|
final title = widget.view.name;
|
||||||
showFlowyMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
|
showHeader: true,
|
||||||
title: title,
|
title: title,
|
||||||
|
showCloseButton: true,
|
||||||
|
showDragHandle: true,
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return AddNewPageWidgetBottomSheet(
|
return AddNewPageWidgetBottomSheet(
|
||||||
view: widget.view,
|
view: widget.view,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/mobile/presentation/widgets/show_flowy_mobile_bottom_sheet.dart';
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||||
import 'package:appflowy/util/theme_mode_extension.dart';
|
import 'package:appflowy/util/theme_mode_extension.dart';
|
||||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
@ -31,9 +31,13 @@ class ThemeSetting extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showFlowyMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
|
showHeader: true,
|
||||||
|
showCloseButton: true,
|
||||||
|
showDragHandle: true,
|
||||||
title: LocaleKeys.settings_appearance_themeMode_label.tr(),
|
title: LocaleKeys.settings_appearance_themeMode_label.tr(),
|
||||||
|
padding: const EdgeInsets.fromLTRB(16, 0, 16, 32),
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
|
@ -45,7 +45,7 @@ class PersonalInfoSettingGroup extends StatelessWidget {
|
|||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context: context,
|
context,
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return EditUsernameBottomSheet(
|
return EditUsernameBottomSheet(
|
||||||
context,
|
context,
|
||||||
|
@ -15,7 +15,7 @@ class SheetPage {
|
|||||||
|
|
||||||
void showPaginatedBottomSheet(BuildContext context, {required SheetPage page}) {
|
void showPaginatedBottomSheet(BuildContext context, {required SheetPage page}) {
|
||||||
showMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context: context,
|
context,
|
||||||
// Workaround for not causing drag to rebuild
|
// Workaround for not causing drag to rebuild
|
||||||
isDragEnabled: false,
|
isDragEnabled: false,
|
||||||
builder: (context) => FlowyBottomSheet(root: page),
|
builder: (context) => FlowyBottomSheet(root: page),
|
||||||
|
@ -106,7 +106,7 @@ class _DateCellState extends GridCellState<GridDateCell> {
|
|||||||
text: child,
|
text: child,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context: context,
|
context,
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return MobileDateCellEditScreen(
|
return MobileDateCellEditScreen(
|
||||||
|
@ -207,7 +207,7 @@ class _SelectOptionWrapState extends State<SelectOptionWrap> {
|
|||||||
text: child,
|
text: child,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context: context,
|
context,
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return MobileSelectOptionEditor(
|
return MobileSelectOptionEditor(
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_block_action_widget.dart';
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_block_action_widget.dart';
|
||||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
@ -67,8 +67,11 @@ class MobileBlockActionButtons extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showBottomSheet(BuildContext context) {
|
void _showBottomSheet(BuildContext context) {
|
||||||
showFlowyMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
|
showHeader: true,
|
||||||
|
showDragHandle: true,
|
||||||
|
showCloseButton: true,
|
||||||
title: LocaleKeys.document_plugins_action.tr(),
|
title: LocaleKeys.document_plugins_action.tr(),
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return BlockActionBottomSheet(
|
return BlockActionBottomSheet(
|
||||||
|
@ -2,7 +2,7 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||||
import 'package:appflowy/plugins/base/emoji/emoji_picker_screen.dart';
|
import 'package:appflowy/plugins/base/emoji/emoji_picker_screen.dart';
|
||||||
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart';
|
||||||
@ -427,11 +427,13 @@ class DocumentCoverState extends State<DocumentCover> {
|
|||||||
IntrinsicWidth(
|
IntrinsicWidth(
|
||||||
child: RoundedTextButton(
|
child: RoundedTextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showFlowyMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
|
showHeader: true,
|
||||||
|
showDragHandle: true,
|
||||||
|
showCloseButton: true,
|
||||||
title:
|
title:
|
||||||
LocaleKeys.document_plugins_cover_changeCover.tr(),
|
LocaleKeys.document_plugins_cover_changeCover.tr(),
|
||||||
isScrollControlled: true,
|
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return ConstrainedBox(
|
return ConstrainedBox(
|
||||||
constraints: const BoxConstraints(
|
constraints: const BoxConstraints(
|
||||||
|
@ -28,7 +28,7 @@ class _EmbedImageUrlWidgetState extends State<EmbedImageUrlWidget> {
|
|||||||
onChanged: (value) => inputText = value,
|
onChanged: (value) => inputText = value,
|
||||||
onEditingComplete: () => widget.onSubmit(inputText),
|
onEditingComplete: () => widget.onSubmit(inputText),
|
||||||
),
|
),
|
||||||
const VSpace(5),
|
const VSpace(8),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 160,
|
width: 160,
|
||||||
child: FlowyButton(
|
child: FlowyButton(
|
||||||
|
@ -2,7 +2,7 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy/workspace/application/settings/application_data_storage.dart';
|
import 'package:appflowy/workspace/application/settings/application_data_storage.dart';
|
||||||
@ -115,10 +115,12 @@ class ImagePlaceholderState extends State<ImagePlaceholder> {
|
|||||||
if (PlatformExtension.isDesktopOrWeb) {
|
if (PlatformExtension.isDesktopOrWeb) {
|
||||||
controller.show();
|
controller.show();
|
||||||
} else {
|
} else {
|
||||||
showFlowyMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
title: LocaleKeys.editor_image.tr(),
|
title: LocaleKeys.editor_image.tr(),
|
||||||
isScrollControlled: true,
|
showHeader: true,
|
||||||
|
showCloseButton: true,
|
||||||
|
showDragHandle: true,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return ConstrainedBox(
|
return ConstrainedBox(
|
||||||
constraints: const BoxConstraints(
|
constraints: const BoxConstraints(
|
||||||
|
@ -2,7 +2,6 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_block_action_widget.dart';
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_block_action_widget.dart';
|
||||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
|
||||||
import 'package:appflowy/plugins/base/color/color_picker_screen.dart';
|
import 'package:appflowy/plugins/base/color/color_picker_screen.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
@ -43,8 +42,12 @@ Future<void> _showBlockActionSheet(
|
|||||||
Node node,
|
Node node,
|
||||||
Selection selection,
|
Selection selection,
|
||||||
) async {
|
) async {
|
||||||
final result = await showFlowyMobileBottomSheet<bool>(
|
final result = await showMobileBottomSheet<bool>(
|
||||||
context,
|
context,
|
||||||
|
showDragHandle: true,
|
||||||
|
showCloseButton: true,
|
||||||
|
showHeader: true,
|
||||||
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 32),
|
||||||
title: LocaleKeys.document_plugins_action.tr(),
|
title: LocaleKeys.document_plugins_action.tr(),
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return BlockActionBottomSheet(
|
return BlockActionBottomSheet(
|
||||||
|
@ -79,6 +79,13 @@ class _TextDecorationMenuState extends State<_TextDecorationMenu> {
|
|||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
widget.editorState.selectionExtraInfo = null;
|
||||||
|
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final children = textDecorations
|
final children = textDecorations
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_edit_link_widget.dart';
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_edit_link_widget.dart';
|
||||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
@ -11,8 +11,11 @@ void showEditLinkBottomSheet(
|
|||||||
void Function(BuildContext context, String text, String href) onEdit,
|
void Function(BuildContext context, String text, String href) onEdit,
|
||||||
) {
|
) {
|
||||||
assert(text.isNotEmpty);
|
assert(text.isNotEmpty);
|
||||||
showFlowyMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
|
showCloseButton: true,
|
||||||
|
showDragHandle: true,
|
||||||
|
showHeader: true,
|
||||||
title: LocaleKeys.editor_editLink.tr(),
|
title: LocaleKeys.editor_editLink.tr(),
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return MobileBottomSheetEditLinkWidget(
|
return MobileBottomSheetEditLinkWidget(
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'package:appflowy/core/config/kv.dart';
|
import 'package:appflowy/core/config/kv.dart';
|
||||||
import 'package:appflowy/core/network_monitor.dart';
|
import 'package:appflowy/core/network_monitor.dart';
|
||||||
import 'package:appflowy/env/cloud_env.dart';
|
import 'package:appflowy/env/cloud_env.dart';
|
||||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
|
||||||
import 'package:appflowy/plugins/document/application/prelude.dart';
|
import 'package:appflowy/plugins/document/application/prelude.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/openai_client.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/openai_client.dart';
|
||||||
@ -164,7 +163,6 @@ void _resolveHomeDeps(GetIt getIt) {
|
|||||||
getIt.registerSingleton(FToast());
|
getIt.registerSingleton(FToast());
|
||||||
|
|
||||||
getIt.registerSingleton(MenuSharedState());
|
getIt.registerSingleton(MenuSharedState());
|
||||||
getIt.registerSingleton(MobileRouterRecord());
|
|
||||||
|
|
||||||
getIt.registerFactoryParam<UserListener, UserProfilePB, void>(
|
getIt.registerFactoryParam<UserListener, UserProfilePB, void>(
|
||||||
(user, _) => UserListener(userProfile: user),
|
(user, _) => UserListener(userProfile: user),
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
export 'recent_service.dart';
|
||||||
|
export 'recent_views_bloc.dart';
|
@ -0,0 +1,61 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:appflowy/core/notification/folder_notification.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-folder2/notification.pb.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-notification/subject.pb.dart';
|
||||||
|
import 'package:appflowy_backend/rust_stream.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
typedef RecentViewsUpdated = void Function(
|
||||||
|
Either<FlowyError, RepeatedViewIdPB> result,
|
||||||
|
);
|
||||||
|
|
||||||
|
class RecentViewsListener {
|
||||||
|
StreamSubscription<SubscribeObject>? _streamSubscription;
|
||||||
|
FolderNotificationParser? _parser;
|
||||||
|
|
||||||
|
RecentViewsUpdated? _recentViewsUpdated;
|
||||||
|
|
||||||
|
void start({
|
||||||
|
RecentViewsUpdated? recentViewsUpdated,
|
||||||
|
}) {
|
||||||
|
_recentViewsUpdated = recentViewsUpdated;
|
||||||
|
_parser = FolderNotificationParser(
|
||||||
|
id: 'recent_views',
|
||||||
|
callback: _observableCallback,
|
||||||
|
);
|
||||||
|
_streamSubscription = RustStreamReceiver.listen(
|
||||||
|
(observable) => _parser?.parse(observable),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _observableCallback(
|
||||||
|
FolderNotification ty,
|
||||||
|
Either<Uint8List, FlowyError> result,
|
||||||
|
) {
|
||||||
|
if (_recentViewsUpdated == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.fold(
|
||||||
|
(payload) {
|
||||||
|
final view = RepeatedViewIdPB.fromBuffer(payload);
|
||||||
|
_recentViewsUpdated?.call(
|
||||||
|
right(view),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(error) => _recentViewsUpdated?.call(
|
||||||
|
left(error),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> stop() async {
|
||||||
|
_parser = null;
|
||||||
|
await _streamSubscription?.cancel();
|
||||||
|
_recentViewsUpdated = null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
|
||||||
|
class RecentService {
|
||||||
|
Future<Either<Unit, FlowyError>> updateRecentViews(
|
||||||
|
List<String> viewIds,
|
||||||
|
bool addInRecent,
|
||||||
|
) async {
|
||||||
|
return FolderEventUpdateRecentViews(
|
||||||
|
UpdateRecentViewPayloadPB(viewIds: viewIds, addInRecent: addInRecent),
|
||||||
|
).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Either<RepeatedViewPB, FlowyError>> readRecentViews() {
|
||||||
|
return FolderEventReadRecentViews().send();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
import 'package:appflowy/workspace/application/recent/recent_listener.dart';
|
||||||
|
import 'package:appflowy/workspace/application/recent/recent_service.dart';
|
||||||
|
import 'package:appflowy_backend/log.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'recent_views_bloc.freezed.dart';
|
||||||
|
|
||||||
|
class RecentViewsBloc extends Bloc<RecentViewsEvent, RecentViewsState> {
|
||||||
|
final _service = RecentService();
|
||||||
|
final _listener = RecentViewsListener();
|
||||||
|
|
||||||
|
RecentViewsBloc() : super(RecentViewsState.initial()) {
|
||||||
|
on<RecentViewsEvent>(
|
||||||
|
(event, emit) async {
|
||||||
|
await event.map(
|
||||||
|
initial: (e) async {
|
||||||
|
_listener.start(
|
||||||
|
recentViewsUpdated: (result) => _onRecentViewsUpdated(
|
||||||
|
result,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
add(const RecentViewsEvent.fetchRecentViews());
|
||||||
|
},
|
||||||
|
addRecentViews: (e) async {
|
||||||
|
await _service.updateRecentViews(e.viewIds, true);
|
||||||
|
},
|
||||||
|
removeRecentViews: (e) async {
|
||||||
|
await _service.updateRecentViews(e.viewIds, false);
|
||||||
|
},
|
||||||
|
fetchRecentViews: (e) async {
|
||||||
|
final result = await _service.readRecentViews();
|
||||||
|
result.fold(
|
||||||
|
(views) => emit(state.copyWith(views: views.items)),
|
||||||
|
(error) => Log.error(error),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() async {
|
||||||
|
await _listener.stop();
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onRecentViewsUpdated(
|
||||||
|
Either<FlowyError, RepeatedViewIdPB> result,
|
||||||
|
) {
|
||||||
|
add(const RecentViewsEvent.fetchRecentViews());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class RecentViewsEvent with _$RecentViewsEvent {
|
||||||
|
const factory RecentViewsEvent.initial() = Initial;
|
||||||
|
const factory RecentViewsEvent.addRecentViews(List<String> viewIds) =
|
||||||
|
AddRecentViews;
|
||||||
|
const factory RecentViewsEvent.removeRecentViews(List<String> viewIds) =
|
||||||
|
RemoveRecentViews;
|
||||||
|
const factory RecentViewsEvent.fetchRecentViews() = FetchRecentViews;
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class RecentViewsState with _$RecentViewsState {
|
||||||
|
const factory RecentViewsState({
|
||||||
|
required List<ViewPB> views,
|
||||||
|
}) = _RecentViewsState;
|
||||||
|
|
||||||
|
factory RecentViewsState.initial() => const RecentViewsState(
|
||||||
|
views: [],
|
||||||
|
);
|
||||||
|
}
|
@ -3,11 +3,14 @@ import 'dart:convert';
|
|||||||
import 'package:appflowy/core/config/kv.dart';
|
import 'package:appflowy/core/config/kv.dart';
|
||||||
import 'package:appflowy/core/config/kv_keys.dart';
|
import 'package:appflowy/core/config/kv_keys.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
|
import 'package:appflowy/workspace/application/favorite/favorite_listener.dart';
|
||||||
|
import 'package:appflowy/workspace/application/recent/recent_service.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_service.dart';
|
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
@ -16,12 +19,14 @@ part 'view_bloc.freezed.dart';
|
|||||||
class ViewBloc extends Bloc<ViewEvent, ViewState> {
|
class ViewBloc extends Bloc<ViewEvent, ViewState> {
|
||||||
final ViewBackendService viewBackendSvc;
|
final ViewBackendService viewBackendSvc;
|
||||||
final ViewListener listener;
|
final ViewListener listener;
|
||||||
|
final FavoriteListener favoriteListener;
|
||||||
final ViewPB view;
|
final ViewPB view;
|
||||||
|
|
||||||
ViewBloc({
|
ViewBloc({
|
||||||
required this.view,
|
required this.view,
|
||||||
}) : viewBackendSvc = ViewBackendService(),
|
}) : viewBackendSvc = ViewBackendService(),
|
||||||
listener = ViewListener(viewId: view.id),
|
listener = ViewListener(viewId: view.id),
|
||||||
|
favoriteListener = FavoriteListener(),
|
||||||
super(ViewState.init(view)) {
|
super(ViewState.init(view)) {
|
||||||
on<ViewEvent>((event, emit) async {
|
on<ViewEvent>((event, emit) async {
|
||||||
await event.map(
|
await event.map(
|
||||||
@ -40,6 +45,17 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
favoriteListener.start(
|
||||||
|
favoritesUpdated: (result, isFavorite) {
|
||||||
|
result.fold((error) {}, (result) {
|
||||||
|
final current =
|
||||||
|
result.items.firstWhereOrNull((v) => v.id == view.id);
|
||||||
|
if (current != null) {
|
||||||
|
add(ViewEvent.viewDidUpdate(left(current)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
final isExpanded = await _getViewIsExpanded(view);
|
final isExpanded = await _getViewIsExpanded(view);
|
||||||
await _loadViewsWhenExpanded(emit, isExpanded);
|
await _loadViewsWhenExpanded(emit, isExpanded);
|
||||||
},
|
},
|
||||||
@ -88,6 +104,7 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
|
|||||||
(error) => state.copyWith(successOrFailure: right(error)),
|
(error) => state.copyWith(successOrFailure: right(error)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
RecentService().updateRecentViews([view.id], false);
|
||||||
},
|
},
|
||||||
duplicate: (e) async {
|
duplicate: (e) async {
|
||||||
final result = await ViewBackendService.duplicate(view: view);
|
final result = await ViewBackendService.duplicate(view: view);
|
||||||
@ -139,6 +156,7 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
|
|||||||
@override
|
@override
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
await listener.stop();
|
await listener.stop();
|
||||||
|
await favoriteListener.stop();
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ class FlowyButton extends StatelessWidget {
|
|||||||
final MainAxisAlignment mainAxisAlignment;
|
final MainAxisAlignment mainAxisAlignment;
|
||||||
final bool showDefaultBoxDecorationOnMobile;
|
final bool showDefaultBoxDecorationOnMobile;
|
||||||
final double iconPadding;
|
final double iconPadding;
|
||||||
|
final bool expand;
|
||||||
|
|
||||||
const FlowyButton({
|
const FlowyButton({
|
||||||
Key? key,
|
Key? key,
|
||||||
@ -50,6 +51,7 @@ class FlowyButton extends StatelessWidget {
|
|||||||
this.mainAxisAlignment = MainAxisAlignment.center,
|
this.mainAxisAlignment = MainAxisAlignment.center,
|
||||||
this.showDefaultBoxDecorationOnMobile = false,
|
this.showDefaultBoxDecorationOnMobile = false,
|
||||||
this.iconPadding = 6,
|
this.iconPadding = 6,
|
||||||
|
this.expand = false,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -113,6 +115,7 @@ class FlowyButton extends StatelessWidget {
|
|||||||
Widget child = Row(
|
Widget child = Row(
|
||||||
mainAxisAlignment: mainAxisAlignment,
|
mainAxisAlignment: mainAxisAlignment,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisSize: expand ? MainAxisSize.max : MainAxisSize.min,
|
||||||
children: children,
|
children: children,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -431,6 +431,17 @@ impl TryInto<MoveNestedViewParams> for MoveNestedViewPayloadPB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, ProtoBuf)]
|
||||||
|
pub struct UpdateRecentViewPayloadPB {
|
||||||
|
#[pb(index = 1)]
|
||||||
|
pub view_ids: Vec<String>,
|
||||||
|
|
||||||
|
// If true, the view will be added to the recent view list.
|
||||||
|
// If false, the view will be removed from the recent view list.
|
||||||
|
#[pb(index = 2)]
|
||||||
|
pub add_in_recent: bool,
|
||||||
|
}
|
||||||
|
|
||||||
// impl<'de> Deserialize<'de> for ViewDataType {
|
// impl<'de> Deserialize<'de> for ViewDataType {
|
||||||
// fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
|
// fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
|
||||||
// where
|
// where
|
||||||
|
@ -158,6 +158,20 @@ pub(crate) async fn toggle_favorites_handler(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn update_recent_views_handler(
|
||||||
|
data: AFPluginData<UpdateRecentViewPayloadPB>,
|
||||||
|
folder: AFPluginState<Weak<FolderManager>>,
|
||||||
|
) -> Result<(), FlowyError> {
|
||||||
|
let params: UpdateRecentViewPayloadPB = data.into_inner();
|
||||||
|
let folder = upgrade_folder(folder)?;
|
||||||
|
if params.add_in_recent {
|
||||||
|
let _ = folder.add_recent_views(params.view_ids).await;
|
||||||
|
} else {
|
||||||
|
let _ = folder.remove_recent_views(params.view_ids).await;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn set_latest_view_handler(
|
pub(crate) async fn set_latest_view_handler(
|
||||||
data: AFPluginData<ViewIdPB>,
|
data: AFPluginData<ViewIdPB>,
|
||||||
folder: AFPluginState<Weak<FolderManager>>,
|
folder: AFPluginState<Weak<FolderManager>>,
|
||||||
|
@ -38,6 +38,7 @@ pub fn init(folder: Weak<FolderManager>) -> AFPlugin {
|
|||||||
.event(FolderEvent::ReadFavorites, read_favorites_handler)
|
.event(FolderEvent::ReadFavorites, read_favorites_handler)
|
||||||
.event(FolderEvent::ReadRecentViews, read_recent_views_handler)
|
.event(FolderEvent::ReadRecentViews, read_recent_views_handler)
|
||||||
.event(FolderEvent::ToggleFavorite, toggle_favorites_handler)
|
.event(FolderEvent::ToggleFavorite, toggle_favorites_handler)
|
||||||
|
.event(FolderEvent::UpdateRecentViews, update_recent_views_handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
|
||||||
@ -149,4 +150,8 @@ pub enum FolderEvent {
|
|||||||
|
|
||||||
#[event(output = "RepeatedViewPB")]
|
#[event(output = "RepeatedViewPB")]
|
||||||
ReadRecentViews = 36,
|
ReadRecentViews = 36,
|
||||||
|
|
||||||
|
// used for add or remove recent views, like history
|
||||||
|
#[event(input = "UpdateRecentViewPayloadPB")]
|
||||||
|
UpdateRecentViews = 37,
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,8 @@ use crate::entities::icon::UpdateViewIconParams;
|
|||||||
use crate::entities::{
|
use crate::entities::{
|
||||||
view_pb_with_child_views, view_pb_without_child_views, ChildViewUpdatePB, CreateViewParams,
|
view_pb_with_child_views, view_pb_without_child_views, ChildViewUpdatePB, CreateViewParams,
|
||||||
CreateWorkspaceParams, DeletedViewPB, FolderSnapshotPB, FolderSnapshotStatePB, FolderSyncStatePB,
|
CreateWorkspaceParams, DeletedViewPB, FolderSnapshotPB, FolderSnapshotStatePB, FolderSyncStatePB,
|
||||||
RepeatedTrashPB, RepeatedViewPB, UpdateViewParams, UserFolderPB, ViewPB, WorkspacePB,
|
RepeatedTrashPB, RepeatedViewIdPB, RepeatedViewPB, UpdateViewParams, UserFolderPB, ViewPB,
|
||||||
WorkspaceSettingPB,
|
WorkspacePB, WorkspaceSettingPB,
|
||||||
};
|
};
|
||||||
use crate::notification::{
|
use crate::notification::{
|
||||||
send_notification, send_workspace_setting_notification, FolderNotification,
|
send_notification, send_workspace_setting_notification, FolderNotification,
|
||||||
@ -782,6 +782,32 @@ impl FolderManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add the view to the recent view list / history.
|
||||||
|
#[tracing::instrument(level = "debug", skip(self), err)]
|
||||||
|
pub async fn add_recent_views(&self, view_ids: Vec<String>) -> FlowyResult<()> {
|
||||||
|
self.with_folder(
|
||||||
|
|| (),
|
||||||
|
|folder| {
|
||||||
|
folder.add_recent_view_ids(view_ids);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.send_update_recent_views_notification().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add the view to the recent view list / history.
|
||||||
|
#[tracing::instrument(level = "debug", skip(self), err)]
|
||||||
|
pub async fn remove_recent_views(&self, view_ids: Vec<String>) -> FlowyResult<()> {
|
||||||
|
self.with_folder(
|
||||||
|
|| (),
|
||||||
|
|folder| {
|
||||||
|
folder.delete_recent_view_ids(view_ids);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.send_update_recent_views_notification().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// Used by toggle_favorites to send notification to frontend, after the favorite status of view has been changed.It sends two distinct notifications: one to correctly update the concerned view's is_favorite status, and another to update the list of favorites that is to be displayed.
|
// Used by toggle_favorites to send notification to frontend, after the favorite status of view has been changed.It sends two distinct notifications: one to correctly update the concerned view's is_favorite status, and another to update the list of favorites that is to be displayed.
|
||||||
async fn send_toggle_favorite_notification(&self, view_id: &str) {
|
async fn send_toggle_favorite_notification(&self, view_id: &str) {
|
||||||
if let Ok(view) = self.get_view_pb(view_id).await {
|
if let Ok(view) = self.get_view_pb(view_id).await {
|
||||||
@ -802,6 +828,15 @@ impl FolderManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn send_update_recent_views_notification(&self) {
|
||||||
|
let recent_views = self.get_all_recent_sections().await;
|
||||||
|
send_notification("recent_views", FolderNotification::DidUpdateRecentViews)
|
||||||
|
.payload(RepeatedViewIdPB {
|
||||||
|
items: recent_views.into_iter().map(|item| item.id).collect(),
|
||||||
|
})
|
||||||
|
.send();
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(self))]
|
#[tracing::instrument(level = "trace", skip(self))]
|
||||||
pub(crate) async fn get_all_favorites(&self) -> Vec<SectionItem> {
|
pub(crate) async fn get_all_favorites(&self) -> Vec<SectionItem> {
|
||||||
self.get_sections(Section::Favorite)
|
self.get_sections(Section::Favorite)
|
||||||
|
@ -33,6 +33,8 @@ pub enum FolderNotification {
|
|||||||
|
|
||||||
DidFavoriteView = 36,
|
DidFavoriteView = 36,
|
||||||
DidUnfavoriteView = 37,
|
DidUnfavoriteView = 37,
|
||||||
|
|
||||||
|
DidUpdateRecentViews = 38,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<FolderNotification> for i32 {
|
impl std::convert::From<FolderNotification> for i32 {
|
||||||
|
Reference in New Issue
Block a user