feat: adjust toggle list, callout, quote and divider on mobile (#3894)

* feat: adjust toggle list block

* feat: show block actions when tapping divider

* feat: add toggle list and callout to toolbar

* feat: refactor the emoji picker button

* fix: toggle list integration tests
This commit is contained in:
Lucas.Xu
2023-11-08 21:10:29 +08:00
committed by GitHub
parent 663f9d3423
commit afc6473582
20 changed files with 308 additions and 108 deletions

View File

@ -15,14 +15,23 @@ void main() {
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();
group('toggle list in document', () { group('toggle list in document', () {
Finder findToggleListIcon({
required bool isExpanded,
}) {
final turns = isExpanded ? 0.25 : 0.0;
return find.byWidgetPredicate(
(widget) => widget is AnimatedRotation && widget.turns == turns,
);
}
void expectToggleListOpened() { void expectToggleListOpened() {
expect(find.byIcon(Icons.arrow_drop_down), findsOneWidget); expect(findToggleListIcon(isExpanded: true), findsOneWidget);
expect(find.byIcon(Icons.arrow_right), findsNothing); expect(findToggleListIcon(isExpanded: false), findsNothing);
} }
void expectToggleListClosed() { void expectToggleListClosed() {
expect(find.byIcon(Icons.arrow_drop_down), findsNothing); expect(findToggleListIcon(isExpanded: false), findsOneWidget);
expect(find.byIcon(Icons.arrow_right), findsOneWidget); expect(findToggleListIcon(isExpanded: true), findsNothing);
} }
testWidgets('convert > to toggle list, and click the icon to close it', testWidgets('convert > to toggle list, and click the icon to close it',
@ -63,7 +72,7 @@ void main() {
expect(find.text(text2, findRichText: true), findsOneWidget); expect(find.text(text2, findRichText: true), findsOneWidget);
// Click the toggle list icon to close it // Click the toggle list icon to close it
final toggleListIcon = find.byIcon(Icons.arrow_drop_down); final toggleListIcon = find.byIcon(Icons.arrow_right);
await tester.tapButton(toggleListIcon); await tester.tapButton(toggleListIcon);
// expect the toggle list to be closed // expect the toggle list to be closed
@ -88,7 +97,7 @@ void main() {
await tester.ime.insertText('> $text'); await tester.ime.insertText('> $text');
// Click the toggle list icon to close it // Click the toggle list icon to close it
final toggleListIcon = find.byIcon(Icons.arrow_drop_down); final toggleListIcon = find.byIcon(Icons.arrow_right);
await tester.tapButton(toggleListIcon); await tester.tapButton(toggleListIcon);
// Press the enter key // Press the enter key
@ -164,7 +173,7 @@ void main() {
// Press the enter key // Press the enter key
// Click the toggle list icon to close it // Click the toggle list icon to close it
final toggleListIcon = find.byIcon(Icons.arrow_drop_down); final toggleListIcon = find.byIcon(Icons.arrow_right);
await tester.tapButton(toggleListIcon); await tester.tapButton(toggleListIcon);
await tester.editor.updateSelection( await tester.editor.updateSelection(

View File

@ -1,22 +1,21 @@
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
import 'package:appflowy/plugins/base/icon/icon_picker_page.dart'; import 'package:appflowy/plugins/base/icon/icon_picker_page.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class MobileEmojiPickerScreen extends StatelessWidget { class MobileEmojiPickerScreen extends StatelessWidget {
static const routeName = '/emoji_picker'; static const routeName = '/emoji_picker';
static const viewId = 'id';
const MobileEmojiPickerScreen({ const MobileEmojiPickerScreen({
super.key, super.key,
required this.id,
}); });
/// view id
final String id;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return IconPickerPage( return IconPickerPage(
id: id, onSelected: (result) {
context.pop<EmojiPickerResult>(result);
},
); );
} }
} }

View File

@ -12,13 +12,23 @@ enum FlowyIconType {
custom; custom;
} }
class EmojiPickerResult {
const EmojiPickerResult(
this.type,
this.emoji,
);
final FlowyIconType type;
final String emoji;
}
class FlowyIconPicker extends StatefulWidget { class FlowyIconPicker extends StatefulWidget {
const FlowyIconPicker({ const FlowyIconPicker({
super.key, super.key,
required this.onSelected, required this.onSelected,
}); });
final void Function(FlowyIconType type, String value) onSelected; final void Function(EmojiPickerResult result) onSelected;
@override @override
State<FlowyIconPicker> createState() => _FlowyIconPickerState(); State<FlowyIconPicker> createState() => _FlowyIconPickerState();
@ -45,7 +55,12 @@ class _FlowyIconPickerState extends State<FlowyIconPicker>
const Spacer(), const Spacer(),
_RemoveIconButton( _RemoveIconButton(
onTap: () { onTap: () {
widget.onSelected(FlowyIconType.icon, ''); widget.onSelected(
const EmojiPickerResult(
FlowyIconType.icon,
'',
),
);
}, },
), ),
], ],
@ -58,7 +73,12 @@ class _FlowyIconPickerState extends State<FlowyIconPicker>
children: [ children: [
FlowyEmojiPicker( FlowyEmojiPicker(
onEmojiSelected: (_, emoji) { onEmojiSelected: (_, emoji) {
widget.onSelected(FlowyIconType.emoji, emoji); widget.onSelected(
EmojiPickerResult(
FlowyIconType.emoji,
emoji,
),
);
}, },
), ),
], ],

View File

@ -1,6 +1,5 @@
import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart'; import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart';
import 'package:appflowy/plugins/base/icon/icon_picker.dart'; import 'package:appflowy/plugins/base/icon/icon_picker.dart';
import 'package:appflowy/workspace/application/view/view_service.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';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
@ -8,11 +7,10 @@ import 'package:go_router/go_router.dart';
class IconPickerPage extends StatefulWidget { class IconPickerPage extends StatefulWidget {
const IconPickerPage({ const IconPickerPage({
super.key, super.key,
required this.id, required this.onSelected,
}); });
/// view id final void Function(EmojiPickerResult) onSelected;
final String id;
@override @override
State<IconPickerPage> createState() => _IconPickerPageState(); State<IconPickerPage> createState() => _IconPickerPageState();
@ -34,13 +32,7 @@ class _IconPickerPageState extends State<IconPickerPage> {
), ),
body: SafeArea( body: SafeArea(
child: FlowyIconPicker( child: FlowyIconPicker(
onSelected: (_, emoji) { onSelected: widget.onSelected,
ViewBackendService.updateViewIcon(
viewId: widget.id,
viewIcon: emoji,
);
context.pop();
},
), ),
), ),
); );

View File

@ -1,3 +1,4 @@
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/mobile_block_action_buttons.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/custom_image_block_component.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/image/custom_image_block_component.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
import 'package:appflowy/plugins/document/presentation/editor_style.dart'; import 'package:appflowy/plugins/document/presentation/editor_style.dart';
@ -26,6 +27,9 @@ Map<String, BlockComponentBuilder> getEditorBuilderMap({
final configuration = BlockComponentConfiguration( final configuration = BlockComponentConfiguration(
padding: (_) => const EdgeInsets.symmetric(vertical: 5.0), padding: (_) => const EdgeInsets.symmetric(vertical: 5.0),
indentPadding: (node, textDirection) => textDirection == TextDirection.ltr
? const EdgeInsets.only(left: 26.0)
: const EdgeInsets.only(right: 26.0),
); );
final customBlockComponentBuilderMap = { final customBlockComponentBuilderMap = {
@ -119,6 +123,14 @@ Map<String, BlockComponentBuilder> getEditorBuilderMap({
DividerBlockKeys.type: DividerBlockComponentBuilder( DividerBlockKeys.type: DividerBlockComponentBuilder(
configuration: configuration, configuration: configuration,
height: 28.0, height: 28.0,
wrapper: (context, node, child) {
return MobileBlockActionButtons(
showThreeDots: false,
node: node,
editorState: editorState,
child: child,
);
},
), ),
MathEquationBlockKeys.type: MathEquationBlockComponentBuilder( MathEquationBlockKeys.type: MathEquationBlockComponentBuilder(
configuration: configuration, configuration: configuration,
@ -146,9 +158,7 @@ Map<String, BlockComponentBuilder> getEditorBuilderMap({
), ),
), ),
errorBlockComponentBuilderKey: ErrorBlockComponentBuilder( errorBlockComponentBuilderKey: ErrorBlockComponentBuilder(
configuration: configuration.copyWith( configuration: configuration,
padding: (_) => const EdgeInsets.symmetric(vertical: 10),
),
), ),
}; };

View File

@ -286,10 +286,8 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
textDecorationMobileToolbarItem, textDecorationMobileToolbarItem,
buildTextAndBackgroundColorMobileToolbarItem(), buildTextAndBackgroundColorMobileToolbarItem(),
headingMobileToolbarItem, headingMobileToolbarItem,
todoListMobileToolbarItem, customListMobileToolbarItem,
listMobileToolbarItem,
linkMobileToolbarItem, linkMobileToolbarItem,
quoteMobileToolbarItem,
dividerMobileToolbarItem, dividerMobileToolbarItem,
imageMobileToolbarItem, imageMobileToolbarItem,
mathEquationMobileToolbarItem, mathEquationMobileToolbarItem,

View File

@ -21,6 +21,7 @@ class MobileBlockActionButtons extends StatelessWidget {
const MobileBlockActionButtons({ const MobileBlockActionButtons({
super.key, super.key,
this.extendActionWidgets = const [], this.extendActionWidgets = const [],
this.showThreeDots = true,
required this.node, required this.node,
required this.editorState, required this.editorState,
required this.child, required this.child,
@ -30,6 +31,7 @@ class MobileBlockActionButtons extends StatelessWidget {
final EditorState editorState; final EditorState editorState;
final List<Widget> extendActionWidgets; final List<Widget> extendActionWidgets;
final Widget child; final Widget child;
final bool showThreeDots;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -37,7 +39,15 @@ class MobileBlockActionButtons extends StatelessWidget {
return child; return child;
} }
const padding = 5.0; if (!showThreeDots) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => _showBottomSheet(context),
child: child,
);
}
const padding = 10.0;
return Stack( return Stack(
children: [ children: [
child, child,

View File

@ -1,7 +1,11 @@
import 'package:appflowy/plugins/base/emoji/emoji_picker_screen.dart';
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/emoji_picker/emoji_picker.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/emoji_picker/emoji_picker.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:appflowy_popover/appflowy_popover.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';
import 'package:go_router/go_router.dart';
class EmojiPickerButton extends StatelessWidget { class EmojiPickerButton extends StatelessWidget {
EmojiPickerButton({ EmojiPickerButton({
@ -15,20 +19,43 @@ class EmojiPickerButton extends StatelessWidget {
final String emoji; final String emoji;
final double emojiSize; final double emojiSize;
final Size emojiPickerSize; final Size emojiPickerSize;
final void Function(String emoji, PopoverController controller) onSubmitted; final void Function(String emoji, PopoverController? controller) onSubmitted;
final PopoverController popoverController = PopoverController(); final PopoverController popoverController = PopoverController();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppFlowyPopover( if (PlatformExtension.isDesktopOrWeb) {
controller: popoverController, return AppFlowyPopover(
triggerActions: PopoverTriggerFlags.click, controller: popoverController,
constraints: BoxConstraints.expand( triggerActions: PopoverTriggerFlags.click,
width: emojiPickerSize.width, constraints: BoxConstraints.expand(
height: emojiPickerSize.height, width: emojiPickerSize.width,
), height: emojiPickerSize.height,
popupBuilder: (context) => _buildEmojiPicker(), ),
child: FlowyTextButton( popupBuilder: (context) => Container(
width: emojiPickerSize.width,
height: emojiPickerSize.height,
padding: const EdgeInsets.all(4.0),
child: EmojiSelectionMenu(
onSubmitted: (emoji) => onSubmitted(emoji, popoverController),
onExit: () {},
),
),
child: FlowyTextButton(
emoji,
overflow: TextOverflow.visible,
fontSize: emojiSize,
padding: EdgeInsets.zero,
constraints: const BoxConstraints(minWidth: 35.0),
fillColor: Colors.transparent,
mainAxisAlignment: MainAxisAlignment.center,
onPressed: () {
popoverController.show();
},
),
);
} else {
return FlowyTextButton(
emoji, emoji,
overflow: TextOverflow.visible, overflow: TextOverflow.visible,
fontSize: emojiSize, fontSize: emojiSize,
@ -36,22 +63,18 @@ class EmojiPickerButton extends StatelessWidget {
constraints: const BoxConstraints(minWidth: 35.0), constraints: const BoxConstraints(minWidth: 35.0),
fillColor: Colors.transparent, fillColor: Colors.transparent,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
onPressed: () { onPressed: () async {
popoverController.show(); final result = await context.push<EmojiPickerResult>(
MobileEmojiPickerScreen.routeName,
);
if (result != null) {
onSubmitted(
result.emoji,
null,
);
}
}, },
), );
); }
}
Widget _buildEmojiPicker() {
return Container(
width: emojiPickerSize.width,
height: emojiPickerSize.height,
padding: const EdgeInsets.all(4.0),
child: EmojiSelectionMenu(
onSubmitted: (emoji) => onSubmitted(emoji, popoverController),
onExit: () {},
),
);
} }
} }

View File

@ -187,7 +187,7 @@ class _CalloutBlockComponentWidgetState
emoji: emoji, emoji: emoji,
onSubmitted: (emoji, controller) { onSubmitted: (emoji, controller) {
setEmoji(emoji); setEmoji(emoji);
controller.close(); controller?.close();
}, },
), ),
), ),

View File

@ -1,6 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/mobile_block_action_buttons.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/startup/startup.dart'; import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/presentation/home/toast.dart'; import 'package:appflowy/workspace/presentation/home/toast.dart';
@ -8,6 +9,7 @@ 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';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class ErrorBlockComponentBuilder extends BlockComponentBuilder { class ErrorBlockComponentBuilder extends BlockComponentBuilder {
ErrorBlockComponentBuilder({ ErrorBlockComponentBuilder({
@ -72,11 +74,15 @@ class _DividerBlockComponentWidgetState extends State<ErrorBlockComponentWidget>
ClipboardServiceData(plainText: jsonEncode(node.toJson())), ClipboardServiceData(plainText: jsonEncode(node.toJson())),
); );
}, },
text: Container( text: SizedBox(
height: 48, height: 52,
alignment: Alignment.center, child: Row(
child: FlowyText( children: [
LocaleKeys.document_errorBlock_theBlockIsNotSupported.tr(), const HSpace(4),
FlowyText(
LocaleKeys.document_errorBlock_theBlockIsNotSupported.tr(),
),
],
), ),
), ),
), ),
@ -95,6 +101,14 @@ class _DividerBlockComponentWidgetState extends State<ErrorBlockComponentWidget>
); );
} }
if (PlatformExtension.isMobile) {
child = MobileBlockActionButtons(
node: node,
editorState: context.read<EditorState>(),
child: child,
);
}
return child; return child;
} }
} }

View File

@ -6,7 +6,6 @@ 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';
import 'package:appflowy/plugins/document/presentation/editor_style.dart'; import 'package:appflowy/plugins/document/presentation/editor_style.dart';
import 'package:appflowy/workspace/application/view/view_bloc.dart';
import 'package:appflowy/workspace/application/view/view_listener.dart'; import 'package:appflowy/workspace/application/view/view_listener.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:appflowy_editor/appflowy_editor.dart';
@ -16,7 +15,6 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/widget/rounded_button.dart'; import 'package:flowy_infra_ui/widget/rounded_button.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 'package:provider/provider.dart';
import 'cover_editor.dart'; import 'cover_editor.dart';
@ -303,15 +301,14 @@ class _DocumentHeaderToolbarState extends State<DocumentHeaderToolbar> {
), ),
onTap: PlatformExtension.isDesktop onTap: PlatformExtension.isDesktop
? null ? null
: () => context.push( : () async {
Uri( final result = await context.push<EmojiPickerResult>(
path: MobileEmojiPickerScreen.routeName, MobileEmojiPickerScreen.routeName,
queryParameters: { );
MobileEmojiPickerScreen.viewId: if (result != null) {
context.read<ViewBloc>().state.view.id, widget.onCoverChanged(icon: result.emoji);
}, }
).toString(), },
),
); );
if (PlatformExtension.isDesktop) { if (PlatformExtension.isDesktop) {
@ -325,8 +322,8 @@ class _DocumentHeaderToolbarState extends State<DocumentHeaderToolbar> {
popupBuilder: (BuildContext popoverContext) { popupBuilder: (BuildContext popoverContext) {
isPopoverOpen = true; isPopoverOpen = true;
return FlowyIconPicker( return FlowyIconPicker(
onSelected: (type, value) { onSelected: (result) {
widget.onCoverChanged(icon: value); widget.onCoverChanged(icon: result.emoji);
_popoverController.close(); _popoverController.close();
}, },
); );
@ -532,8 +529,8 @@ class _DocumentIconState extends State<DocumentIcon> {
child: child, child: child,
popupBuilder: (BuildContext popoverContext) { popupBuilder: (BuildContext popoverContext) {
return FlowyIconPicker( return FlowyIconPicker(
onSelected: (type, value) { onSelected: (result) {
widget.onIconChanged(value); widget.onIconChanged(result.emoji);
_popoverController.close(); _popoverController.close();
}, },
); );
@ -542,15 +539,14 @@ class _DocumentIconState extends State<DocumentIcon> {
} else { } else {
child = GestureDetector( child = GestureDetector(
child: child, child: child,
onTap: () => context.push( onTap: () async {
Uri( final result = await context.push<EmojiPickerResult>(
path: MobileEmojiPickerScreen.routeName, MobileEmojiPickerScreen.routeName,
queryParameters: { );
MobileEmojiPickerScreen.viewId: if (result != null) {
context.read<ViewBloc>().state.view.id, widget.onIconChanged(result.emoji);
}, }
).toString(), },
),
); );
} }

View File

@ -0,0 +1,121 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
final customListMobileToolbarItem = MobileToolbarItem.withMenu(
itemIcon: const AFMobileIcon(afMobileIcons: AFMobileIcons.list),
itemMenuBuilder: (editorState, selection, _) {
return _MobileListMenu(
editorState: editorState,
selection: selection,
);
},
);
class _MobileListMenu extends StatelessWidget {
const _MobileListMenu({
required this.editorState,
required this.selection,
});
final Selection selection;
final EditorState editorState;
@override
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 2,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
childAspectRatio: 5,
shrinkWrap: true,
children: [
// bulleted list, numbered list
_buildListButton(
context,
BulletedListBlockKeys.type,
const AFMobileIcon(afMobileIcons: AFMobileIcons.bulletedList),
LocaleKeys.document_plugins_bulletedList.tr(),
),
_buildListButton(
context,
NumberedListBlockKeys.type,
const AFMobileIcon(afMobileIcons: AFMobileIcons.numberedList),
LocaleKeys.document_plugins_numberedList.tr(),
),
// todo list, quote list
_buildListButton(
context,
TodoListBlockKeys.type,
const AFMobileIcon(afMobileIcons: AFMobileIcons.checkbox),
LocaleKeys.document_plugins_todoList.tr(),
),
_buildListButton(
context,
QuoteBlockKeys.type,
const AFMobileIcon(afMobileIcons: AFMobileIcons.quote),
LocaleKeys.document_plugins_quoteList.tr(),
),
// toggle list, callout
_buildListButton(
context,
ToggleListBlockKeys.type,
const FlowySvg(
FlowySvgs.toggle_list_s,
size: Size.square(24),
),
LocaleKeys.document_plugins_toggleList.tr(),
),
_buildListButton(
context,
CalloutBlockKeys.type,
const Icon(Icons.note_rounded),
LocaleKeys.document_plugins_callout.tr(),
),
],
);
}
Widget _buildListButton(
BuildContext context,
String listBlockType,
Widget icon,
String label,
) {
final node = editorState.getNodeAtPath(selection.start.path);
final type = node?.type;
if (node == null || type == null) {
const SizedBox.shrink();
}
final isSelected = type == listBlockType;
return MobileToolbarItemMenuBtn(
icon: icon,
label: FlowyText(label),
isSelected: isSelected,
onPressed: () async {
await editorState.formatNode(
selection,
(node) {
final attributes = {
ParagraphBlockKeys.delta: (node.delta ?? Delta()).toJson(),
if (listBlockType == TodoListBlockKeys.type)
TodoListBlockKeys.checked: false,
if (listBlockType == CalloutBlockKeys.type)
CalloutBlockKeys.icon: '📌',
};
return node.copyWith(
type: isSelected ? ParagraphBlockKeys.type : listBlockType,
attributes: attributes,
);
},
);
},
);
}
}

View File

@ -25,7 +25,8 @@ export 'image/mobile_image_toolbar_item.dart';
export 'inline_math_equation/inline_math_equation.dart'; export 'inline_math_equation/inline_math_equation.dart';
export 'inline_math_equation/inline_math_equation_toolbar_item.dart'; export 'inline_math_equation/inline_math_equation_toolbar_item.dart';
export 'math_equation/math_equation_block_component.dart'; export 'math_equation/math_equation_block_component.dart';
export 'math_equation/mobile_math_eqaution_toolbar_item.dart'; export 'math_equation/mobile_math_equation_toolbar_item.dart';
export 'mobile_toolbar_item/list_mobile_toolbar_item.dart';
export 'openai/widgets/auto_completion_node_widget.dart'; export 'openai/widgets/auto_completion_node_widget.dart';
export 'openai/widgets/smart_edit_node_widget.dart'; export 'openai/widgets/smart_edit_node_widget.dart';
export 'openai/widgets/smart_edit_toolbar_item.dart'; export 'openai/widgets/smart_edit_toolbar_item.dart';

View File

@ -166,13 +166,17 @@ class _ToggleListBlockComponentWidgetState
Container( Container(
constraints: const BoxConstraints(minWidth: 26, minHeight: 22), constraints: const BoxConstraints(minWidth: 26, minHeight: 22),
padding: const EdgeInsets.only(right: 4.0), padding: const EdgeInsets.only(right: 4.0),
child: FlowyIconButton( child: AnimatedRotation(
width: 18.0, turns: collapsed ? 0.0 : 0.25,
icon: Icon( duration: const Duration(milliseconds: 200),
collapsed ? Icons.arrow_right : Icons.arrow_drop_down, child: FlowyIconButton(
size: 18.0, width: 18.0,
icon: const Icon(
Icons.arrow_right,
size: 18.0,
),
onPressed: onCollapsed,
), ),
onPressed: onCollapsed,
), ),
), ),

View File

@ -209,11 +209,8 @@ GoRoute _mobileEmojiPickerPageRoute() {
parentNavigatorKey: AppGlobals.rootNavKey, parentNavigatorKey: AppGlobals.rootNavKey,
path: MobileEmojiPickerScreen.routeName, path: MobileEmojiPickerScreen.routeName,
pageBuilder: (context, state) { pageBuilder: (context, state) {
final id = state.uri.queryParameters[MobileEmojiPickerScreen.viewId]!; return const MaterialPage(
return MaterialPage( child: MobileEmojiPickerScreen(),
child: MobileEmojiPickerScreen(
id: id,
),
); );
}, },
); );

View File

@ -361,10 +361,10 @@ class _SingleInnerViewItemState extends State<SingleInnerViewItem> {
popupBuilder: (context) { popupBuilder: (context) {
isIconPickerOpened = true; isIconPickerOpened = true;
return FlowyIconPicker( return FlowyIconPicker(
onSelected: (_, emoji) { onSelected: (result) {
ViewBackendService.updateViewIcon( ViewBackendService.updateViewIcon(
viewId: widget.view.id, viewId: widget.view.id,
viewIcon: emoji, viewIcon: result.emoji,
); );
controller.close(); controller.close();
}, },

View File

@ -54,8 +54,8 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: a47fc6f ref: "50117b6"
resolved-ref: a47fc6fc712b06991f578ae2ab314cbe23034e96 resolved-ref: "50117b6900e4b239603ee48f6f3e7b7bc603c865"
url: "https://github.com/AppFlowy-IO/appflowy-editor.git" url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
source: git source: git
version: "1.5.2" version: "1.5.2"

View File

@ -47,7 +47,8 @@ dependencies:
appflowy_editor: appflowy_editor:
git: git:
url: https://github.com/AppFlowy-IO/appflowy-editor.git url: https://github.com/AppFlowy-IO/appflowy-editor.git
ref: a47fc6f ref: 50117b6
appflowy_popover: appflowy_popover:
path: packages/appflowy_popover path: packages/appflowy_popover

View File

@ -616,7 +616,12 @@
"smartEditDisabled": "Connect OpenAI in Settings", "smartEditDisabled": "Connect OpenAI in Settings",
"discardResponse": "Do you want to discard the AI responses?", "discardResponse": "Do you want to discard the AI responses?",
"createInlineMathEquation": "Create equation", "createInlineMathEquation": "Create equation",
"toggleList": "Toggle List", "toggleList": "Toggle list",
"quoteList":"Quote list",
"numberedList":"Numbered list",
"bulletedList":"Bulleted list",
"todoList": "Todo List",
"callout": "Callout",
"cover": { "cover": {
"changeCover": "Change Cover", "changeCover": "Change Cover",
"colors": "Colors", "colors": "Colors",