fix: legacy bugs on mobile (#4927)

This commit is contained in:
Lucas.Xu 2024-03-18 13:06:12 +07:00 committed by GitHub
parent fdc105a3e8
commit 7375349626
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 286 additions and 85 deletions

View File

@ -77,7 +77,7 @@ class AppBarDoneButton extends StatelessWidget {
Widget build(BuildContext context) {
return AppBarButton(
onTap: onTap,
padding: const EdgeInsets.fromLTRB(12, 12, 8, 12),
padding: const EdgeInsets.all(12),
child: FlowyText(
LocaleKeys.button_done.tr(),
color: Theme.of(context).colorScheme.primary,
@ -93,7 +93,7 @@ class AppBarSaveButton extends StatelessWidget {
super.key,
required this.onTap,
this.enable = true,
this.padding = const EdgeInsets.fromLTRB(12, 12, 8, 12),
this.padding = const EdgeInsets.all(12),
});
final VoidCallback onTap;
@ -165,7 +165,7 @@ class AppBarMoreButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AppBarButton(
padding: const EdgeInsets.fromLTRB(12, 12, 8, 12),
padding: const EdgeInsets.all(12),
onTap: () => onTap(context),
child: const FlowySvg(FlowySvgs.three_dots_s),
);

View File

@ -4,7 +4,7 @@ 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/widgets/flowy_mobile_state_container.dart';
import 'package:appflowy/plugins/base/emoji/emoji_text.dart';
import 'package:appflowy/plugins/document/document_page.dart';
import 'package:appflowy/plugins/document/presentation/editor_notification.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
@ -144,6 +144,8 @@ class _MobileViewPageState extends State<MobileViewPage> {
Widget _buildAppBarMoreButton(ViewPB view) {
return AppBarMoreButton(
onTap: (context) {
EditorNotification.exitEditing().post();
showMobileBottomSheet(
context,
showDragHandle: true,
@ -183,14 +185,12 @@ class _MobileViewPageState extends State<MobileViewPage> {
context.read<FavoriteBloc>().add(FavoriteEvent.toggle(view));
break;
case MobileViewBottomSheetBodyAction.undo:
context.dispatchNotification(
const EditorNotification(type: EditorNotificationType.redo),
);
EditorNotification.undo().post();
context.pop();
break;
case MobileViewBottomSheetBodyAction.redo:
EditorNotification.redo().post();
context.pop();
context.dispatchNotification(EditorNotification.redo());
break;
case MobileViewBottomSheetBodyAction.helpCenter:
// unimplemented

View File

@ -0,0 +1,82 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
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 BottomSheetCloseButton extends StatelessWidget {
const BottomSheetCloseButton({
super.key,
this.onTap,
});
final VoidCallback? onTap;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap ?? () => Navigator.pop(context),
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0),
child: SizedBox(
width: 18,
height: 18,
child: FlowySvg(
FlowySvgs.m_bottom_sheet_close_m,
),
),
),
);
}
}
class BottomSheetDoneButton extends StatelessWidget {
const BottomSheetDoneButton({
super.key,
this.onDone,
});
final VoidCallback? onDone;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onDone ?? () => Navigator.pop(context),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12.0),
child: FlowyText(
LocaleKeys.button_done.tr(),
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.w500,
textAlign: TextAlign.right,
),
),
);
}
}
class BottomSheetBackButton extends StatelessWidget {
const BottomSheetBackButton({
super.key,
this.onTap,
});
final VoidCallback? onTap;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap ?? () => Navigator.pop(context),
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0),
child: SizedBox(
width: 18,
height: 18,
child: FlowySvg(
FlowySvgs.m_app_bar_back_s,
),
),
),
);
}
}

View File

@ -1,6 +1,4 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_buttons.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
@ -26,7 +24,7 @@ class BottomSheetHeader extends StatelessWidget {
left: 0,
child: Align(
alignment: Alignment.centerLeft,
child: AppBarCloseButton(
child: BottomSheetCloseButton(
onTap: onClose,
),
),
@ -41,19 +39,8 @@ class BottomSheetHeader extends StatelessWidget {
if (onDone != null)
Align(
alignment: Alignment.centerRight,
child: FlowyButton(
useIntrinsicWidth: true,
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 5),
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10)),
color: Color(0xFF00BCF0),
),
text: FlowyText.medium(
LocaleKeys.button_done.tr(),
color: Colors.white,
fontSize: 16.0,
),
onTap: onDone,
child: BottomSheetDoneButton(
onDone: onDone,
),
),
],

View File

@ -1,4 +1,4 @@
import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_buttons.dart';
import 'package:appflowy/plugins/base/drag_handler.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart' hide WidgetBuilder;
import 'package:flutter/material.dart';
@ -195,12 +195,12 @@ class BottomSheetHeader extends StatelessWidget {
if (showBackButton)
const Align(
alignment: Alignment.centerLeft,
child: AppBarBackButton(),
child: BottomSheetBackButton(),
),
if (showCloseButton)
const Align(
alignment: Alignment.centerLeft,
child: AppBarCloseButton(),
child: BottomSheetCloseButton(),
),
Align(
child: FlowyText(
@ -212,8 +212,8 @@ class BottomSheetHeader extends StatelessWidget {
if (showDoneButton)
Align(
alignment: Alignment.centerRight,
child: AppBarDoneButton(
onTap: () => Navigator.pop(context),
child: BottomSheetDoneButton(
onDone: () => Navigator.pop(context),
),
),
],

View File

@ -43,6 +43,7 @@ class MobileSettingItem extends StatelessWidget {
trailing: trailing,
onTap: onTap,
visualDensity: VisualDensity.compact,
contentPadding: const EdgeInsets.only(left: 8.0),
),
);
}

View File

@ -1,8 +1,7 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/application/doc_bloc.dart';
import 'package:appflowy/plugins/document/presentation/banner.dart';
import 'package:appflowy/plugins/document/presentation/editor_notification.dart';
import 'package:appflowy/plugins/document/presentation/editor_page.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
@ -15,24 +14,9 @@ import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
enum EditorNotificationType {
undo,
redo,
}
class EditorNotification extends Notification {
const EditorNotification({
required this.type,
});
EditorNotification.undo() : type = EditorNotificationType.undo;
EditorNotification.redo() : type = EditorNotificationType.redo;
final EditorNotificationType type;
}
class DocumentPage extends StatefulWidget {
const DocumentPage({
super.key,
@ -50,12 +34,23 @@ class DocumentPage extends StatefulWidget {
}
class _DocumentPageState extends State<DocumentPage> {
EditorState? editorState;
@override
void initState() {
super.initState();
// The appflowy editor use Intl as localization, set the default language as fallback.
Intl.defaultLocale = 'en_US';
EditorNotification.addListener(_onEditorNotification);
}
@override
void dispose() {
EditorNotification.removeListener(_onEditorNotification);
super.dispose();
}
@override
@ -75,6 +70,7 @@ class _DocumentPageState extends State<DocumentPage> {
}
final editorState = state.editorState;
this.editorState = editorState;
final error = state.error;
if (error != null || editorState == null) {
Log.error(error);
@ -149,20 +145,19 @@ class _DocumentPageState extends State<DocumentPage> {
);
}
// Future<void> _exportPage(DocumentDataPB data) async {
// final picker = getIt<FilePickerService>();
// final dir = await picker.getDirectoryPath();
// if (dir == null) {
// return;
// }
// final path = p.join(dir, '${documentBloc.view.name}.json');
// const encoder = JsonEncoder.withIndent(' ');
// final json = encoder.convert(data.toProto3Json());
// await File(path).writeAsString(json.base64.base64);
// if (mounted) {
// showSnackBarMessage(context, 'Export success to $path');
// }
// }
void _onEditorNotification(EditorNotificationType type) {
final editorState = this.editorState;
if (editorState == null) {
return;
}
if (type == EditorNotificationType.undo) {
undoCommand.execute(editorState);
} else if (type == EditorNotificationType.redo) {
redoCommand.execute(editorState);
} else if (type == EditorNotificationType.exitEditing) {
editorState.selection = null;
}
}
void _onNotificationAction(
BuildContext context,

View File

@ -0,0 +1,46 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
enum EditorNotificationType {
none,
undo,
redo,
exitEditing,
}
class EditorNotification {
const EditorNotification({
required this.type,
});
EditorNotification.undo() : type = EditorNotificationType.undo;
EditorNotification.redo() : type = EditorNotificationType.redo;
EditorNotification.exitEditing() : type = EditorNotificationType.exitEditing;
static final PropertyValueNotifier<EditorNotificationType> _notifier =
PropertyValueNotifier(
EditorNotificationType.none,
);
final EditorNotificationType type;
void post() {
_notifier.value = type;
}
static void addListener(ValueChanged<EditorNotificationType> listener) {
_notifier.addListener(() {
listener(_notifier.value);
});
}
static void removeListener(ValueChanged<EditorNotificationType> listener) {
_notifier.removeListener(() {
listener(_notifier.value);
});
}
static void dispose() {
_notifier.dispose();
}
}

View File

@ -197,7 +197,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
_initEditorL10n();
_initializeShortcuts();
appFlowyEditorAutoScrollEdgeOffset = 250;
appFlowyEditorAutoScrollEdgeOffset = 220;
indentableBlockTypes.add(ToggleListBlockKeys.type);
convertibleBlockTypes.add(ToggleListBlockKeys.type);
slashMenuItems = _customSlashMenuItems();
@ -317,19 +317,12 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
editorState: editorState,
editorScrollController: editorScrollController,
toolbarBuilder: (context, anchor, closeToolbar) {
return AdaptiveTextSelectionToolbar.editable(
clipboardStatus: ClipboardStatus.pasteable,
onCopy: () {
customCopyCommand.execute(editorState);
closeToolbar();
},
onCut: () => customCutCommand.execute(editorState),
onPaste: () => customPasteCommand.execute(editorState),
onSelectAll: () => selectAllCommand.execute(editorState),
onLiveTextInput: null,
onLookUp: null,
onSearchWeb: null,
onShare: null,
return AdaptiveTextSelectionToolbar.buttonItems(
buttonItems: buildMobileFloatingToolbarItems(
editorState,
anchor,
closeToolbar,
),
anchors: TextSelectionToolbarAnchors(
primaryAnchor: anchor,
),

View File

@ -0,0 +1,84 @@
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';
List<ContextMenuButtonItem> buildMobileFloatingToolbarItems(
EditorState editorState,
Offset offset,
Function closeToolbar,
) {
// copy, paste, select, select all, cut
final selection = editorState.selection;
if (selection == null) {
return [];
}
final toolbarItems = <ContextMenuButtonItem>[];
if (!selection.isCollapsed) {
toolbarItems.add(
ContextMenuButtonItem(
label: LocaleKeys.editor_copy.tr(),
onPressed: () {
copyCommand.execute(editorState);
closeToolbar();
},
),
);
}
toolbarItems.add(
ContextMenuButtonItem(
label: LocaleKeys.editor_paste.tr(),
onPressed: () {
pasteCommand.execute(editorState);
closeToolbar();
},
),
);
if (!selection.isCollapsed) {
toolbarItems.add(
ContextMenuButtonItem(
label: LocaleKeys.editor_cut.tr(),
onPressed: () {
cutCommand.execute(editorState);
closeToolbar();
},
),
);
}
toolbarItems.add(
ContextMenuButtonItem(
label: LocaleKeys.editor_select.tr(),
onPressed: () {
editorState.selectWord(offset);
closeToolbar();
},
),
);
toolbarItems.add(
ContextMenuButtonItem(
label: LocaleKeys.editor_selectAll.tr(),
onPressed: () {
selectAllCommand.execute(editorState);
closeToolbar();
},
),
);
return toolbarItems;
}
extension on EditorState {
void selectWord(Offset offset) {
final node = service.selectionService.getNodeInOffset(offset);
final selection = node?.selectable?.getWordBoundaryInOffset(offset);
if (selection == null) {
return;
}
updateSelectionWithReason(selection);
}
}

View File

@ -9,6 +9,8 @@ import 'package:flowy_infra/size.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
const _count = 6;
Future<void> showTextColorAndBackgroundColorPicker(
BuildContext context, {
required EditorState editorState,
@ -26,7 +28,7 @@ Future<void> showTextColorAndBackgroundColorPicker(
backgroundColor: theme.toolbarMenuBackgroundColor,
elevation: 20,
title: LocaleKeys.grid_selectOption_colorPanelTitle.tr(),
padding: const EdgeInsets.fromLTRB(18, 4, 18, 8),
padding: const EdgeInsets.fromLTRB(10, 4, 10, 8),
builder: (context) {
return _TextColorAndBackgroundColor(
editorState: editorState,
@ -79,6 +81,7 @@ class _TextColorAndBackgroundColorState
fontSize: 14.0,
),
),
const VSpace(6.0),
_TextColors(
selectedColor: selectedTextColor?.tryToColor(),
onSelectedColor: (textColor) async {
@ -115,6 +118,7 @@ class _TextColorAndBackgroundColorState
fontSize: 14.0,
),
),
const VSpace(6.0),
_BackgroundColors(
selectedColor: selectedBackgroundColor?.tryToColor(),
onSelectedColor: (backgroundColor) async {
@ -202,7 +206,7 @@ class _BackgroundColors extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 6,
crossAxisCount: _count,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: colors.mapIndexed(
@ -236,9 +240,7 @@ class _BackgroundColorItem extends StatelessWidget {
return GestureDetector(
onTap: onTap,
child: Container(
margin: const EdgeInsets.all(
6.0,
),
margin: const EdgeInsets.all(6.0),
decoration: BoxDecoration(
color: color,
borderRadius: Corners.s12Border,
@ -283,7 +285,7 @@ class _TextColors extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 6,
crossAxisCount: _count,
shrinkWrap: true,
padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(),
@ -317,9 +319,7 @@ class _TextColorItem extends StatelessWidget {
return GestureDetector(
onTap: onTap,
child: Container(
margin: const EdgeInsets.all(
6.0,
),
margin: const EdgeInsets.all(6.0),
decoration: BoxDecoration(
borderRadius: Corners.s12Border,
border: Border.all(

View File

@ -29,6 +29,7 @@ export 'link_preview/link_preview_cache.dart';
export 'link_preview/link_preview_menu.dart';
export 'math_equation/math_equation_block_component.dart';
export 'math_equation/mobile_math_equation_toolbar_item.dart';
export 'mobile_floating_toolbar/custom_mobile_floating_toolbar.dart';
export 'mobile_toolbar_v3/aa_toolbar_item.dart';
export 'mobile_toolbar_v3/add_block_toolbar_item.dart';
export 'mobile_toolbar_v3/appflowy_mobile_toolbar.dart';

View File

@ -0,0 +1,5 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="&#229;&#133;&#179;&#233;&#151;&#173;_close 1">
<path id="Union" d="M4.13807 3.19526C3.87772 2.93491 3.45561 2.93491 3.19526 3.19526C2.93491 3.45561 2.93491 3.87772 3.19526 4.13807L8.05719 9L3.19526 13.8619C2.93491 14.1223 2.93491 14.5444 3.19526 14.8047C3.45561 15.0651 3.87772 15.0651 4.13807 14.8047L9 9.94281L13.8619 14.8047C14.1223 15.0651 14.5444 15.0651 14.8047 14.8047C15.0651 14.5444 15.0651 14.1223 14.8047 13.8619L9.94281 9L14.8047 4.13807C15.0651 3.87772 15.0651 3.45561 14.8047 3.19526C14.5444 2.93491 14.1223 2.93491 13.8619 3.19526L9 8.05719L4.13807 3.19526Z" fill="#1F2329" stroke="#1F2329" stroke-width="0.24" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 790 B

View File

@ -0,0 +1,5 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="&#229;&#133;&#179;&#233;&#151;&#173;_close 1">
<path id="Union" d="M4.13807 3.19526C3.87772 2.93491 3.45561 2.93491 3.19526 3.19526C2.93491 3.45561 2.93491 3.87772 3.19526 4.13807L8.05719 9L3.19526 13.8619C2.93491 14.1223 2.93491 14.5444 3.19526 14.8047C3.45561 15.0651 3.87772 15.0651 4.13807 14.8047L9 9.94281L13.8619 14.8047C14.1223 15.0651 14.5444 15.0651 14.8047 14.8047C15.0651 14.5444 15.0651 14.1223 14.8047 13.8619L9.94281 9L14.8047 4.13807C15.0651 3.87772 15.0651 3.45561 14.8047 3.19526C14.5444 2.93491 14.1223 2.93491 13.8619 3.19526L9 8.05719L4.13807 3.19526Z" fill="#1F2329" stroke="#1F2329" stroke-width="0.24" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 790 B

View File

@ -1309,6 +1309,8 @@
"copy": "Copy",
"paste": "Paste",
"find": "Find",
"select": "Select",
"selectAll": "Select all",
"previousMatch": "Previous match",
"nextMatch": "Next match",
"closeFind": "Close",