diff --git a/frontend/appflowy_flutter/assets/images/common/archive.svg b/frontend/appflowy_flutter/assets/images/common/archive.svg new file mode 100644 index 0000000000..590dad7c38 --- /dev/null +++ b/frontend/appflowy_flutter/assets/images/common/archive.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/appflowy_flutter/assets/images/editor/Align/center.svg b/frontend/appflowy_flutter/assets/images/editor/align/center.svg similarity index 100% rename from frontend/appflowy_flutter/assets/images/editor/Align/center.svg rename to frontend/appflowy_flutter/assets/images/editor/align/center.svg diff --git a/frontend/appflowy_flutter/assets/images/editor/Align/left.svg b/frontend/appflowy_flutter/assets/images/editor/align/left.svg similarity index 100% rename from frontend/appflowy_flutter/assets/images/editor/Align/left.svg rename to frontend/appflowy_flutter/assets/images/editor/align/left.svg diff --git a/frontend/appflowy_flutter/assets/images/editor/Align/right.svg b/frontend/appflowy_flutter/assets/images/editor/align/right.svg similarity index 100% rename from frontend/appflowy_flutter/assets/images/editor/Align/right.svg rename to frontend/appflowy_flutter/assets/images/editor/align/right.svg diff --git a/frontend/appflowy_flutter/assets/translations/en.json b/frontend/appflowy_flutter/assets/translations/en.json index f4da7002e9..1ce3a2c762 100644 --- a/frontend/appflowy_flutter/assets/translations/en.json +++ b/frontend/appflowy_flutter/assets/translations/en.json @@ -6,6 +6,8 @@ "subscribeNewsletterText": "Subscribe to Newsletter", "letsGoButtonText": "Quick Start", "title": "Title", + "youCanAlso": "You can also", + "and": "and", "signUp": { "buttonText": "Sign Up", "title": "Sign Up to @:appName", @@ -196,6 +198,7 @@ "selectFiles": "Select the files that need to be export", "createNewFolder": "Create a new folder", "createNewFolderDesc": "Tell us where you want to store your data", + "defineWhereYourDataIsStored": "Define where your data is stored", "open": "Open", "openFolder": "Open an existing folder", "openFolderDesc": "Read and write it to your existing AppFlowy folder", @@ -204,6 +207,7 @@ "locationDesc": "Pick a name for your AppFlowy data folder", "browser": "Browse", "create": "Create", + "set": "Set", "folderPath": "Path to store your folder", "locationCannotBeEmpty": "Path cannot be empty", "pathCopiedSnackbar": "File storage path copied to clipboard!", @@ -420,7 +424,12 @@ "turnInto": "Turn into", "moveUp": "Move up", "moveDown": "Move down", - "color": "Color" + "color": "Color", + "align": "Align", + "left": "Left", + "center": "Center", + "right": "Right", + "defaultColor": "Default" } } }, diff --git a/frontend/appflowy_flutter/lib/plugins/document/application/editor_transaction_adapter.dart b/frontend/appflowy_flutter/lib/plugins/document/application/editor_transaction_adapter.dart index fac2d75223..eccbcf31f2 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/application/editor_transaction_adapter.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/application/editor_transaction_adapter.dart @@ -1,6 +1,5 @@ import 'package:appflowy/plugins/document/application/document_data_pb_extension.dart'; import 'package:appflowy/plugins/document/application/doc_service.dart'; -import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/protobuf/flowy-document2/protobuf.dart'; import 'package:appflowy_editor/appflowy_editor.dart' show @@ -31,11 +30,12 @@ class TransactionAdapter { final String documentId; Future apply(Transaction transaction, EditorState editorState) async { + // Log.debug('transaction => ${transaction.toJson()}'); final actions = transaction.operations .map((op) => op.toBlockAction(editorState)) .whereNotNull() .expand((element) => element); - Log.debug('actions => $actions'); + // Log.debug('actions => $actions'); await documentService.applyAction( documentId: documentId, actions: actions, @@ -85,6 +85,12 @@ extension on InsertOperation { ..action = BlockActionTypePB.Insert ..payload = payload, ); + if (node.children.isNotEmpty) { + final childrenActions = node.children + .map((e) => InsertOperation(e.path, [e]).toBlockAction(editorState)) + .expand((element) => element); + actions.addAll(childrenActions); + } previousNode = node; } return actions; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart index 85f432cfcb..3cf4001f1f 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart @@ -24,16 +24,6 @@ class AppFlowyEditorPage extends StatefulWidget { class _AppFlowyEditorPageState extends State { final scrollController = ScrollController(); - final slashMenuItems = [ - boardMenuItem, - gridMenuItem, - calloutItem, - dividerMenuItem, - mathEquationItem, - codeBlockItem, - emojiMenuItem, - autoGeneratorMenuItem, - ]; final List commandShortcutEvents = [ ...codeBlockCommands, @@ -53,6 +43,19 @@ class _AppFlowyEditorPageState extends State { highlightColorItem, ]; + late final slashMenuItems = [ + dividerMenuItem, + inlineGridMenuItem(documentBloc), + referenceGridMenuItem, + inlineBoardMenuItem(documentBloc), + boardMenuItem, + calloutItem, + mathEquationItem, + codeBlockItem, + emojiMenuItem, + autoGeneratorMenuItem, + ]; + late final Map blockComponentBuilders = _customAppFlowyBlockComponentBuilders(); late final List characterShortcutEvents = [ @@ -170,7 +173,9 @@ class _AppFlowyEditorPageState extends State { ), textStyleBuilder: (level) => styleCustomizer.headingStyleBuilder(level), ), - ImageBlockKeys.type: ImageBlockComponentBuilder(), + ImageBlockKeys.type: ImageBlockComponentBuilder( + configuration: configuration, + ), BoardBlockKeys.type: BoardBlockComponentBuilder( configuration: configuration, ), @@ -225,14 +230,24 @@ class _AppFlowyEditorPageState extends State { CalloutBlockKeys.type ]; + final supportAlignBuilderType = [ + ImageBlockKeys.type, + ]; + final colorAction = [ OptionAction.divider, OptionAction.color, ]; + final alignAction = [ + OptionAction.divider, + OptionAction.align, + ]; + final List actions = [ ...standardActions, if (supportColorBuilderTypes.contains(entry.key)) ...colorAction, + if (supportAlignBuilderType.contains(entry.key)) ...alignAction, ]; builder.showActions = (_) => true; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_button.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_button.dart index e1dafe214a..4a33a72653 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_button.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_button.dart @@ -24,14 +24,15 @@ class BlockOptionButton extends StatelessWidget { @override Widget build(BuildContext context) { final popoverActions = actions.map((e) { - if (e == OptionAction.divider) { - return DividerOptionAction(); - } else if (e == OptionAction.color) { - return ColorOptionAction( - editorState: editorState, - ); - } else { - return OptionActionWrapper(e); + switch (e) { + case OptionAction.divider: + return DividerOptionAction(); + case OptionAction.color: + return ColorOptionAction(editorState: editorState); + case OptionAction.align: + return AlignOptionAction(editorState: editorState); + default: + return OptionActionWrapper(e); } }).toList(); @@ -119,6 +120,7 @@ class BlockOptionButton extends StatelessWidget { case OptionAction.moveDown: transaction.moveNode(node.path.next.next, node); break; + case OptionAction.align: case OptionAction.color: case OptionAction.divider: throw UnimplementedError(); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option_action.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option_action.dart index 2e15776b59..1c23c0b2ba 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option_action.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option_action.dart @@ -1,12 +1,12 @@ import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/type_option/select_option_editor.dart'; -import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart'; -import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:appflowy_editor/appflowy_editor.dart' hide FlowySvg; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/image.dart'; +import 'package:flowy_infra/theme_extension.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; import 'package:styled_widget/styled_widget.dart'; @@ -18,6 +18,90 @@ enum OptionAction { moveDown, color, divider, + align; + + String get assetName { + switch (this) { + case OptionAction.delete: + return 'editor/delete'; + case OptionAction.duplicate: + return 'editor/duplicate'; + case OptionAction.turnInto: + return 'editor/turn_into'; + case OptionAction.moveUp: + return 'editor/move_up'; + case OptionAction.moveDown: + return 'editor/move_down'; + case OptionAction.color: + return 'editor/color'; + case OptionAction.divider: + return 'editor/divider'; + case OptionAction.align: + return 'editor/align/center'; + } + } + + String get description { + switch (this) { + case OptionAction.delete: + return LocaleKeys.document_plugins_optionAction_delete.tr(); + case OptionAction.duplicate: + return LocaleKeys.document_plugins_optionAction_duplicate.tr(); + case OptionAction.turnInto: + return LocaleKeys.document_plugins_optionAction_turnInto.tr(); + case OptionAction.moveUp: + return LocaleKeys.document_plugins_optionAction_moveUp.tr(); + case OptionAction.moveDown: + return LocaleKeys.document_plugins_optionAction_moveDown.tr(); + case OptionAction.color: + return LocaleKeys.document_plugins_optionAction_color.tr(); + case OptionAction.align: + return LocaleKeys.document_plugins_optionAction_align.tr(); + case OptionAction.divider: + throw UnsupportedError('Divider does not have description'); + } + } +} + +enum OptionAlignType { + left, + center, + right; + + static OptionAlignType fromString(String? value) { + switch (value) { + case 'left': + return OptionAlignType.left; + case 'center': + return OptionAlignType.center; + case 'right': + return OptionAlignType.right; + default: + return OptionAlignType.center; + } + } + + String get assetName { + switch (this) { + case OptionAlignType.left: + return 'editor/align/left'; + case OptionAlignType.center: + return 'editor/align/center'; + case OptionAlignType.right: + return 'editor/align/right'; + } + } + + String get description { + switch (this) { + case OptionAlignType.left: + return LocaleKeys.document_plugins_optionAction_left.tr(); + case OptionAlignType.center: + return LocaleKeys.document_plugins_optionAction_center.tr(); + case OptionAlignType.right: + return LocaleKeys.document_plugins_optionAction_right.tr(); + } + } } class DividerOptionAction extends CustomActionCell { @@ -30,6 +114,98 @@ class DividerOptionAction extends CustomActionCell { } } +class AlignOptionAction extends PopoverActionCell { + AlignOptionAction({ + required this.editorState, + }); + + final EditorState editorState; + + @override + Widget? leftIcon(Color iconColor) { + return FlowySvg( + name: align.assetName, + size: const Size.square(12), + ).padding(all: 2.0); + } + + @override + String get name { + return LocaleKeys.document_plugins_optionAction_align.tr(); + } + + @override + PopoverActionCellBuilder get builder => + (context, parentController, controller) { + final selection = editorState.selection?.normalized; + if (selection == null) { + return const SizedBox.shrink(); + } + final node = editorState.getNodeAtPath(selection.start.path); + if (node == null) { + return const SizedBox.shrink(); + } + final children = buildAlignOptions(context, (align) async { + await onAlignChanged(align); + controller.close(); + parentController.close(); + }); + return IntrinsicHeight( + child: IntrinsicWidth( + child: Column( + children: children, + ), + ), + ); + }; + + List buildAlignOptions( + BuildContext context, + void Function(OptionAlignType) onTap, + ) { + return OptionAlignType.values.map((e) => OptionAlignWrapper(e)).map((e) { + final leftIcon = e.leftIcon(Theme.of(context).colorScheme.onSurface); + final rightIcon = e.rightIcon(Theme.of(context).colorScheme.onSurface); + return HoverButton( + onTap: () => onTap(e.inner), + itemHeight: ActionListSizes.itemHeight, + leftIcon: leftIcon, + name: e.name, + rightIcon: rightIcon, + ); + }).toList(); + } + + OptionAlignType get align { + final selection = editorState.selection; + if (selection == null) { + return OptionAlignType.center; + } + final node = editorState.getNodeAtPath(selection.start.path); + final align = node?.attributes['align']; + return OptionAlignType.fromString(align); + } + + Future onAlignChanged(OptionAlignType align) async { + if (align == this.align) { + return; + } + final selection = editorState.selection; + if (selection == null) { + return; + } + final node = editorState.getNodeAtPath(selection.start.path); + if (node == null) { + return; + } + final transaction = editorState.transaction; + transaction.updateNode(node, { + 'align': align.name, + }); + await editorState.apply(transaction); + } +} + class ColorOptionAction extends PopoverActionCell { ColorOptionAction({ required this.editorState, @@ -46,60 +222,61 @@ class ColorOptionAction extends PopoverActionCell { } @override - String get name { - return LocaleKeys.toolbar_color.tr(); - } + String get name => LocaleKeys.document_plugins_optionAction_color.tr(); @override - Widget Function(BuildContext context, PopoverController controller) - get builder => (context, controller) { - final selection = editorState.selection?.normalized; - if (selection == null) { - return const SizedBox.shrink(); - } - final node = editorState.getNodeAtPath(selection.start.path); - if (node == null) { - return const SizedBox.shrink(); - } - final bgColor = - node.attributes[blockComponentBackgroundColor] as String?; - final selectedColor = convertHexToSelectOptionColorPB( - bgColor, - context, - ); - - return SelectOptionColorList( - selectedColor: selectedColor, - onSelectedColor: (color) { - controller.close(); - - final nodes = editorState.getNodesInSelection(selection); - final transaction = editorState.transaction; - for (final node in nodes) { - transaction.updateNode(node, { - blockComponentBackgroundColor: - color.toColor(context).toHex(), - }); - } - editorState.apply(transaction); - }, - ); - }; - - SelectOptionColorPB? convertHexToSelectOptionColorPB( - String? hexColor, + Widget Function( BuildContext context, - ) { - if (hexColor == null) { - return null; - } - for (final value in SelectOptionColorPB.values) { - if (value.toColor(context).toHex() == hexColor) { - return value; - } - } - return null; - } + PopoverController parentController, + PopoverController controller, + ) get builder => (context, parentController, controller) { + final selection = editorState.selection?.normalized; + if (selection == null) { + return const SizedBox.shrink(); + } + final node = editorState.getNodeAtPath(selection.start.path); + if (node == null) { + return const SizedBox.shrink(); + } + final bgColor = + node.attributes[blockComponentBackgroundColor] as String?; + final selectedColor = bgColor?.toColor(); + + final colors = [ + // clear background color. + FlowyColorOption( + color: Colors.transparent, + name: LocaleKeys.document_plugins_optionAction_defaultColor.tr(), + ), + ...FlowyTint.values.map( + (e) => FlowyColorOption( + color: e.color(context), + name: e.tintName(AppFlowyEditorLocalizations.current), + ), + ), + ]; + + return FlowyColorPicker( + colors: colors, + selected: selectedColor, + border: Border.all( + color: Theme.of(context).colorScheme.onBackground, + width: 1, + ), + onTap: (color, index) async { + final transaction = editorState.transaction; + final backgroundColor = + color == Colors.transparent ? null : color.toHex(); + transaction.updateNode(node, { + blockComponentBackgroundColor: backgroundColor, + }); + await editorState.apply(transaction); + + controller.close(); + parentController.close(); + }, + ); + }; } class OptionActionWrapper extends ActionCell { @@ -108,60 +285,20 @@ class OptionActionWrapper extends ActionCell { final OptionAction inner; @override - Widget? leftIcon(Color iconColor) { - var name = ''; - // TODO: add icons. - switch (inner) { - case OptionAction.delete: - name = 'editor/delete'; - break; - case OptionAction.duplicate: - name = 'editor/duplicate'; - break; - case OptionAction.turnInto: - name = 'editor/turn_into'; - break; - case OptionAction.moveUp: - name = 'editor/move_up'; - break; - case OptionAction.moveDown: - name = 'editor/move_down'; - break; - default: - throw UnimplementedError(); - } - if (name.isEmpty) { - return null; - } - return FlowySvg(name: name); - } + Widget? leftIcon(Color iconColor) => FlowySvg(name: inner.assetName); @override - String get name { - var description = ''; - switch (inner) { - // TODO: l10n - case OptionAction.delete: - description = LocaleKeys.document_plugins_optionAction_delete.tr(); - break; - case OptionAction.duplicate: - description = LocaleKeys.document_plugins_optionAction_duplicate.tr(); - break; - case OptionAction.turnInto: - description = LocaleKeys.document_plugins_optionAction_turnInto.tr(); - break; - case OptionAction.moveUp: - description = LocaleKeys.document_plugins_optionAction_moveUp.tr(); - break; - case OptionAction.moveDown: - description = LocaleKeys.document_plugins_optionAction_moveDown.tr(); - break; - case OptionAction.color: - description = LocaleKeys.document_plugins_optionAction_color.tr(); - break; - case OptionAction.divider: - throw UnimplementedError(); - } - return description; - } + String get name => inner.description; +} + +class OptionAlignWrapper extends ActionCell { + OptionAlignWrapper(this.inner); + + final OptionAlignType inner; + + @override + Widget? leftIcon(Color iconColor) => FlowySvg(name: inner.assetName); + + @override + String get name => inner.description; } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option_action_button.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option_action_button.dart index c1faca11a7..1bbce0b5ad 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option_action_button.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option_action_button.dart @@ -102,6 +102,7 @@ class OptionActionList extends StatelessWidget { case OptionAction.moveDown: transaction.moveNode(node.path.next.next, node); break; + case OptionAction.align: case OptionAction.color: case OptionAction.divider: throw UnimplementedError(); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/insert_page_command.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/insert_page_command.dart index b30b4eb9f7..614cc7f618 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/insert_page_command.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/insert_page_command.dart @@ -15,7 +15,31 @@ class DatabaseBlockKeys { } extension InsertDatabase on EditorState { - Future insertPage(ViewPB parentView, ViewPB childView) async { + Future insertInlinePage(String parentViewId, ViewPB childView) async { + final selection = this.selection; + if (selection == null || !selection.isCollapsed) { + return; + } + final node = getNodeAtPath(selection.end.path); + if (node == null) { + return; + } + + final transaction = this.transaction; + transaction.insertNode( + selection.end.path, + Node( + type: _convertPageType(childView), + attributes: { + DatabaseBlockKeys.parentID: parentViewId, + DatabaseBlockKeys.viewID: childView.id, + }, + ), + ); + await apply(transaction); + } + + Future insertReferencePage(ViewPB childView) async { final selection = this.selection; if (selection == null || !selection.isCollapsed) { return; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/link_to_page_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/link_to_page_widget.dart index 55d633260d..d4925b5920 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/link_to_page_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/link_to_page_widget.dart @@ -35,7 +35,7 @@ void showLinkToPageMenu( layoutType: pageType, hintText: pageType.toHintText(), onSelected: (appPB, viewPB) { - editorState.insertPage(appPB, viewPB); + editorState.insertReferencePage(viewPB); linkToPageMenuEntry.remove(); }, ), diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/board/board_view_menu_item.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/board/board_view_menu_item.dart index 4d78ba4f55..1fa4cbe5c9 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/board/board_view_menu_item.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/board/board_view_menu_item.dart @@ -7,7 +7,7 @@ import 'package:appflowy/plugins/document/application/prelude.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/base/insert_page_command.dart'; import 'package:easy_localization/easy_localization.dart'; -SelectionMenuItem boardViewMenuItem(DocumentBloc documentBloc) => +SelectionMenuItem inlineBoardMenuItem(DocumentBloc documentBloc) => SelectionMenuItem( name: LocaleKeys.document_slashMenu_board_createANewBoard.tr(), icon: (editorState, onSelected, style) => SelectableSvgWidget( @@ -15,43 +15,21 @@ SelectionMenuItem boardViewMenuItem(DocumentBloc documentBloc) => isSelected: onSelected, style: style, ), - keywords: ['board', 'kanban'], + keywords: ['board', 'kanban', 'database'], handler: (editorState, menuService, context) async { if (!documentBloc.view.hasParentViewId()) { return; } - final appId = documentBloc.view.parentViewId; - final service = ViewBackendService(); - - final result = (await ViewBackendService.createView( - parentViewId: appId, + final parentViewId = documentBloc.view.parentViewId; + ViewBackendService.createView( + parentViewId: parentViewId, name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(), layoutType: ViewLayoutPB.Board, - )) - .getLeftOrNull(); - - // If the result is null, then something went wrong here. - if (result == null) { - return; - } - - final app = (await service.getView(result.viewId)).getLeftOrNull(); - // We should show an error dialog. - if (app == null) { - return; - } - - final view = (await service.getChildView( - parentViewId: result.viewId, - childViewId: result.id, - )) - .getLeftOrNull(); - // As this. - if (view == null) { - return; - } - - editorState.insertPage(app, view); + ).then( + (value) => value + .swap() + .map((r) => editorState.insertInlinePage(parentViewId, r)), + ); }, ); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/callout/callout_block_component.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/callout/callout_block_component.dart index a486ad8363..8eb97c6d41 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/callout/callout_block_component.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/callout/callout_block_component.dart @@ -51,7 +51,7 @@ SelectionMenuItem calloutItem = SelectionMenuItem.node( nodeBuilder: (editorState) => calloutNode(), replace: (_, node) => node.delta?.isEmpty ?? false, updateSelection: (_, path, __, ___) { - return Selection.single(path: [...path, 0], startOffset: 0); + return Selection.single(path: path, startOffset: 0); }, ); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/grid/grid_menu_item.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/grid/grid_menu_item.dart index 94216d1a6a..0b6b1450ba 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/grid/grid_menu_item.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/grid/grid_menu_item.dart @@ -6,14 +6,14 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -SelectionMenuItem gridMenuItem = SelectionMenuItem( +SelectionMenuItem referenceGridMenuItem = SelectionMenuItem( name: LocaleKeys.document_plugins_referencedGrid.tr(), icon: (editorState, onSelected, style) => SelectableSvgWidget( - name: 'editor/board', + name: 'editor/grid', isSelected: onSelected, style: style, ), - keywords: ['referenced', 'grid'], + keywords: ['referenced', 'grid', 'database'], handler: (editorState, menuService, context) { final container = Overlay.of(context); showLinkToPageMenu( diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/grid/grid_view_menu_item.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/grid/grid_view_menu_item.dart index 84c32c5a41..e12499e013 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/grid/grid_view_menu_item.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/grid/grid_view_menu_item.dart @@ -7,7 +7,7 @@ import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:easy_localization/easy_localization.dart'; -SelectionMenuItem gridViewMenuItem(DocumentBloc documentBloc) => +SelectionMenuItem inlineGridMenuItem(DocumentBloc documentBloc) => SelectionMenuItem( name: LocaleKeys.document_slashMenu_grid_createANewGrid.tr(), icon: (editorState, onSelected, style) => SelectableSvgWidget( @@ -15,43 +15,22 @@ SelectionMenuItem gridViewMenuItem(DocumentBloc documentBloc) => isSelected: onSelected, style: style, ), - keywords: ['grid'], + keywords: ['grid', 'database'], handler: (editorState, menuService, context) async { if (!documentBloc.view.hasParentViewId()) { return; } - final appId = documentBloc.view.parentViewId; - final service = ViewBackendService(); - - final result = (await ViewBackendService.createView( - parentViewId: appId, + final parentViewId = documentBloc.view.parentViewId; + ViewBackendService.createView( + parentViewId: parentViewId, + openAfterCreate: false, name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(), layoutType: ViewLayoutPB.Grid, - )) - .getLeftOrNull(); - - // If the result is null, then something went wrong here. - if (result == null) { - return; - } - - final app = (await service.getView(result.viewId)).getLeftOrNull(); - // We should show an error dialog. - if (app == null) { - return; - } - - final view = (await service.getChildView( - parentViewId: result.viewId, - childViewId: result.id, - )) - .getLeftOrNull(); - // As this. - if (view == null) { - return; - } - - editorState.insertPage(app, view); + ).then( + (value) => value + .swap() + .map((r) => editorState.insertInlinePage(parentViewId, r)), + ); }, ); diff --git a/frontend/appflowy_flutter/lib/startup/tasks/app_window_size_manager.dart b/frontend/appflowy_flutter/lib/startup/tasks/app_window_size_manager.dart index 8fcef3723d..59c3cb4e59 100644 --- a/frontend/appflowy_flutter/lib/startup/tasks/app_window_size_manager.dart +++ b/frontend/appflowy_flutter/lib/startup/tasks/app_window_size_manager.dart @@ -8,12 +8,15 @@ import 'package:appflowy/startup/startup.dart'; class WindowSizeManager { static const double minWindowHeight = 400.0; - static const double minWindowWidth = 600.0; + static const double minWindowWidth = 800.0; + + static const width = 'width'; + static const height = 'height'; Future saveSize(Size size) async { final windowSize = { - 'height': max(size.height, minWindowHeight), - 'width': max(size.width, minWindowWidth), + height: max(size.height, minWindowHeight), + width: max(size.width, minWindowWidth), }; await getIt().set( @@ -23,14 +26,11 @@ class WindowSizeManager { } Future getSize() async { - const defaultWindowSize = '{"height": 600.0, "width": 800.0}'; + final defaultWindowSize = jsonEncode({height: 600.0, width: 800.0}); final windowSize = await getIt().get(KVKeys.windowSize); final size = json.decode( - windowSize.fold( - (l) => defaultWindowSize, - (r) => r, - ), + windowSize.getOrElse(() => defaultWindowSize), ); - return Size(size['width']!, size['height']!); + return Size(size[width]!, size[height]!); } } diff --git a/frontend/appflowy_flutter/lib/startup/tasks/windows.dart b/frontend/appflowy_flutter/lib/startup/tasks/windows.dart index 2f64443e1d..c01e9412a3 100644 --- a/frontend/appflowy_flutter/lib/startup/tasks/windows.dart +++ b/frontend/appflowy_flutter/lib/startup/tasks/windows.dart @@ -34,7 +34,6 @@ class InitAppWindowTask extends LaunchTask with WindowListener { WindowSizeManager.minWindowWidth, WindowSizeManager.minWindowHeight, ), - center: true, title: title, ); diff --git a/frontend/appflowy_flutter/lib/user/presentation/folder/folder_widget.dart b/frontend/appflowy_flutter/lib/user/presentation/folder/folder_widget.dart index 20eb65fbcd..12b3e58193 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/folder/folder_widget.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/folder/folder_widget.dart @@ -2,9 +2,13 @@ import 'dart:io'; import 'package:appflowy/util/file_picker/file_picker_service.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flowy_infra/image.dart'; +import 'package:flowy_infra/size.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flowy_infra_ui/widget/buttons/secondary_button.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; +import 'package:google_fonts/google_fonts.dart'; import '../../../generated/locale_keys.g.dart'; import '../../../startup/startup.dart'; @@ -19,9 +23,9 @@ enum _FolderPage { class FolderWidget extends StatefulWidget { const FolderWidget({ - Key? key, + super.key, required this.createFolderCallback, - }) : super(key: key); + }); final Future Function() createFolderCallback; @@ -41,9 +45,6 @@ class _FolderWidgetState extends State { switch (page) { case _FolderPage.options: return FolderOptionsWidget( - onPressedCreate: () { - setState(() => page = _FolderPage.create); - }, onPressedOpen: () { _openFolder(); }, @@ -65,43 +66,36 @@ class _FolderWidgetState extends State { if (path != null) { await getIt().setPath(path); await widget.createFolderCallback(); + setState(() {}); } } } class FolderOptionsWidget extends StatelessWidget { const FolderOptionsWidget({ - Key? key, - required this.onPressedCreate, + super.key, required this.onPressedOpen, - }) : super(key: key); + }); - final VoidCallback onPressedCreate; final VoidCallback onPressedOpen; @override Widget build(BuildContext context) { - return Column( - children: [ - _FolderCard( - title: LocaleKeys.settings_files_createNewFolder.tr(), - subtitle: LocaleKeys.settings_files_createNewFolderDesc.tr(), + return FutureBuilder( + future: getIt().getPath(), + builder: (context, result) { + final subtitle = result.hasData ? result.data! : ''; + return _FolderCard( + icon: const FlowySvg(name: 'common/archive'), + title: LocaleKeys.settings_files_defineWhereYourDataIsStored.tr(), + subtitle: subtitle, trailing: _buildTextButton( context, - LocaleKeys.settings_files_create.tr(), - onPressedCreate, - ), - ), - _FolderCard( - title: LocaleKeys.settings_files_openFolder.tr(), - subtitle: LocaleKeys.settings_files_openFolderDesc.tr(), - trailing: _buildTextButton( - context, - LocaleKeys.settings_files_open.tr(), + LocaleKeys.settings_files_set.tr(), onPressedOpen, ), - ), - ], + ); + }, ); } } @@ -224,27 +218,24 @@ Widget _buildTextButton( String title, VoidCallback onPressed, ) { - return FlowyTextButton( + return SecondaryTextButton( title, + mode: SecondaryTextButtonMode.small, onPressed: onPressed, - fillColor: Theme.of(context).colorScheme.primary, - fontColor: Theme.of(context).colorScheme.onPrimary, - hoverColor: Theme.of(context).colorScheme.primaryContainer, ); } class _FolderCard extends StatelessWidget { const _FolderCard({ - Key? key, required this.title, required this.subtitle, this.trailing, - }) : super(key: key); + this.icon, + }); final String title; - final String subtitle; - + final Widget? icon; final Widget? trailing; @override @@ -252,31 +243,35 @@ class _FolderCard extends StatelessWidget { return Card( child: Padding( padding: const EdgeInsets.symmetric( - vertical: 4.0, + vertical: 16.0, horizontal: 16.0, ), child: Row( children: [ + if (icon != null) + Padding( + padding: const EdgeInsets.only(right: 20), + child: icon!, + ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - FlowyText.medium( + FlowyText.regular( title, + fontSize: FontSizes.s14, + fontFamily: GoogleFonts.poppins( + fontWeight: FontWeight.w500, + ).fontFamily, ), - Row( - children: [ - Flexible( - child: Text( - subtitle, - overflow: TextOverflow.ellipsis, - style: - Theme.of(context).textTheme.bodyMedium!.copyWith( - fontWeight: FontWeight.w400, - ), - ), - ), - ], + const VSpace(4), + FlowyText.regular( + subtitle, + overflow: TextOverflow.ellipsis, + fontSize: FontSizes.s12, + fontFamily: GoogleFonts.poppins( + fontWeight: FontWeight.w300, + ).fontFamily, ), ], ), diff --git a/frontend/appflowy_flutter/lib/user/presentation/skip_log_in_screen.dart b/frontend/appflowy_flutter/lib/user/presentation/skip_log_in_screen.dart index bb04f39ec0..50a1fd3441 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/skip_log_in_screen.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/skip_log_in_screen.dart @@ -1,5 +1,7 @@ import 'package:appflowy/core/frameless_window.dart'; import 'package:appflowy/startup/entry_point.dart'; +import 'package:appflowy/startup/launch_configuration.dart'; +import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/user/application/auth/auth_service.dart'; import 'package:dartz/dartz.dart' as dartz; import 'package:easy_localization/easy_localization.dart'; @@ -11,11 +13,10 @@ 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-user/user_profile.pb.dart'; import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../generated/locale_keys.g.dart'; -import '../../startup/launch_configuration.dart'; -import '../../startup/startup.dart'; import 'folder/folder_widget.dart'; import 'router.dart'; import 'widgets/background.dart'; @@ -35,6 +36,8 @@ class SkipLogInScreen extends StatefulWidget { } class _SkipLogInScreenState extends State { + var _didCustomizeFolder = false; + @override Widget build(BuildContext context) { return Scaffold( @@ -48,60 +51,64 @@ class _SkipLogInScreenState extends State { Widget _renderBody(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, children: [ FlowyLogoTitle( title: LocaleKeys.welcomeText.tr(), - logoSize: const Size.square(60), + logoSize: const Size.square(40), ), - const VSpace(40), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - GoButton( - onPressed: () => _autoRegister(context), - ), - ], + const VSpace(32), + GoButton( + onPressed: () { + if (_didCustomizeFolder) { + _relaunchAppAndAutoRegister(); + } else { + _autoRegister(context); + } + }, ), - const VSpace(20), + const VSpace(32), + _buildSubscribeButtons(context), + const VSpace(32), SizedBox( - width: MediaQuery.of(context).size.width * 0.8, + width: MediaQuery.of(context).size.width * 0.5, child: FolderWidget( createFolderCallback: () async { - await FlowyRunner.run( - FlowyApp(), - config: const LaunchConfiguration( - autoRegistrationSupported: true, - ), - ); + _didCustomizeFolder = true; }, ), ), - const Spacer(), - _buildSubscribeButtons(context), + const VSpace(64), ], ); } - Row _buildSubscribeButtons(BuildContext context) { + Widget _buildSubscribeButtons(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ + FlowyText.regular( + LocaleKeys.youCanAlso.tr(), + fontSize: FontSizes.s12, + ), FlowyTextButton( LocaleKeys.githubStarText.tr(), fontWeight: FontWeight.w500, fontColor: Theme.of(context).colorScheme.primary, - decoration: TextDecoration.underline, hoverColor: Colors.transparent, fillColor: Colors.transparent, - onPressed: () => - _launchURL('https://github.com/AppFlowy-IO/appflowy'), + onPressed: () => _launchURL( + 'https://github.com/AppFlowy-IO/appflowy', + ), + ), + FlowyText.regular( + LocaleKeys.and.tr(), + fontSize: FontSizes.s12, ), - const HSpace(20), FlowyTextButton( LocaleKeys.subscribeNewsletterText.tr(), fontWeight: FontWeight.w500, fontColor: Theme.of(context).colorScheme.primary, - decoration: TextDecoration.underline, hoverColor: Colors.transparent, fillColor: Colors.transparent, onPressed: () => _launchURL('https://www.appflowy.io/blog'), @@ -110,7 +117,7 @@ class _SkipLogInScreenState extends State { ); } - _launchURL(String url) async { + Future _launchURL(String url) async { final uri = Uri.parse(url); if (await canLaunchUrl(uri)) { await launchUrl(uri); @@ -133,6 +140,15 @@ class _SkipLogInScreenState extends State { ); } + Future _relaunchAppAndAutoRegister() async { + await FlowyRunner.run( + FlowyApp(), + config: const LaunchConfiguration( + autoRegistrationSupported: true, + ), + ); + } + void _openCurrentWorkspace( BuildContext context, UserProfilePB user, @@ -162,8 +178,14 @@ class GoButton extends StatelessWidget { Widget build(BuildContext context) { return FlowyTextButton( LocaleKeys.letsGoButtonText.tr(), - fontSize: FontSizes.s16, - padding: const EdgeInsets.symmetric(horizontal: 80, vertical: 12.0), + constraints: const BoxConstraints( + maxWidth: 340, + maxHeight: 48, + ), + mainAxisAlignment: MainAxisAlignment.center, + fontSize: FontSizes.s14, + fontFamily: GoogleFonts.poppins(fontWeight: FontWeight.w500).fontFamily, + padding: const EdgeInsets.symmetric(vertical: 14.0), onPressed: onPressed, fillColor: Theme.of(context).colorScheme.primary, fontColor: Theme.of(context).colorScheme.onPrimary, diff --git a/frontend/appflowy_flutter/lib/user/presentation/widgets/background.dart b/frontend/appflowy_flutter/lib/user/presentation/widgets/background.dart index f7353ebd5a..779e7cf0ba 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/widgets/background.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/widgets/background.dart @@ -5,6 +5,7 @@ import 'package:flowy_infra/size.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; class AuthFormContainer extends StatelessWidget { final List children; @@ -43,12 +44,14 @@ class FlowyLogoTitle extends StatelessWidget { children: [ SizedBox.fromSize( size: logoSize, - child: svgWidget("flowy_logo"), + child: svgWidget('flowy_logo'), ), - const VSpace(30), - FlowyText.semibold( + const VSpace(40), + FlowyText.regular( title, fontSize: FontSizes.s24, + fontFamily: + GoogleFonts.poppins(fontWeight: FontWeight.w500).fontFamily, color: Theme.of(context).colorScheme.tertiary, ), ], diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_system_view.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_system_view.dart index 6cf682503f..fe13da5360 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_system_view.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_system_view.dart @@ -1,5 +1,6 @@ import 'package:appflowy/workspace/presentation/settings/widgets/settings_export_file_widget.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/settings_file_customize_location_view.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; class SettingsFileSystemView extends StatefulWidget { @@ -14,7 +15,8 @@ class SettingsFileSystemView extends StatefulWidget { class _SettingsFileSystemViewState extends State { late final _items = [ const SettingsFileLocationCustomizer(), - const SettingsExportFileWidget() + // disable export data for v0.2.0 in release mode. + if (kDebugMode) const SettingsExportFileWidget() ]; @override diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/pop_up_action.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/pop_up_action.dart index 2d1d699a2d..6fc14bda64 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/pop_up_action.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/pop_up_action.dart @@ -74,8 +74,7 @@ class _PopoverActionListState ); } else if (action is PopoverActionCell) { return PopoverActionCellWidget( - mutex: widget.mutex, - // popoverController: popoverController, + popoverController: popoverController, action: action, itemHeight: ActionListSizes.itemHeight, ); @@ -104,13 +103,18 @@ abstract class ActionCell extends PopoverAction { String get name; } +typedef PopoverActionCellBuilder = Widget Function( + BuildContext context, + PopoverController parentController, + PopoverController controller, +); + abstract class PopoverActionCell extends PopoverAction { Widget? leftIcon(Color iconColor) => null; Widget? rightIcon(Color iconColor) => null; String get name; - Widget Function(BuildContext context, PopoverController controller) - get builder; + PopoverActionCellBuilder get builder; } abstract class CustomActionCell extends PopoverAction { @@ -146,7 +150,7 @@ class ActionCellWidget extends StatelessWidget { final rightIcon = actionCell.rightIcon(Theme.of(context).colorScheme.onSurface); - return _HoverButton( + return HoverButton( itemHeight: itemHeight, leftIcon: leftIcon, rightIcon: rightIcon, @@ -156,24 +160,30 @@ class ActionCellWidget extends StatelessWidget { } } -class PopoverActionCellWidget extends StatelessWidget { - PopoverActionCellWidget({ - Key? key, - this.mutex, - // required this.popoverController, +class PopoverActionCellWidget extends StatefulWidget { + const PopoverActionCellWidget({ + super.key, + required this.popoverController, required this.action, required this.itemHeight, - }) : super(key: key); + }); final T action; final double itemHeight; - final PopoverMutex? mutex; - final PopoverController popoverController = PopoverController(); + final PopoverController popoverController; + @override + State createState() => + _PopoverActionCellWidgetState(); +} + +class _PopoverActionCellWidgetState + extends State> { + final popoverController = PopoverController(); @override Widget build(BuildContext context) { - final actionCell = action as PopoverActionCell; + final actionCell = widget.action as PopoverActionCell; final leftIcon = actionCell.leftIcon(Theme.of(context).colorScheme.onSurface); final rightIcon = @@ -183,10 +193,11 @@ class PopoverActionCellWidget extends StatelessWidget { asBarrier: true, popupBuilder: (context) => actionCell.builder( context, + widget.popoverController, popoverController, ), - child: _HoverButton( - itemHeight: itemHeight, + child: HoverButton( + itemHeight: widget.itemHeight, leftIcon: leftIcon, rightIcon: rightIcon, name: actionCell.name, @@ -196,8 +207,9 @@ class PopoverActionCellWidget extends StatelessWidget { } } -class _HoverButton extends StatelessWidget { - const _HoverButton({ +class HoverButton extends StatelessWidget { + const HoverButton({ + super.key, required this.onTap, required this.itemHeight, required this.leftIcon, diff --git a/frontend/appflowy_flutter/macos/Runner.xcodeproj/project.pbxproj b/frontend/appflowy_flutter/macos/Runner.xcodeproj/project.pbxproj index 66748b368b..de6bf2b7c8 100644 --- a/frontend/appflowy_flutter/macos/Runner.xcodeproj/project.pbxproj +++ b/frontend/appflowy_flutter/macos/Runner.xcodeproj/project.pbxproj @@ -434,7 +434,7 @@ "@executable_path/../Frameworks", ); ONLY_ACTIVE_ARCH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.macos; + PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.appflowy.flutter; PRODUCT_NAME = AppFlowy; PROVISIONING_PROFILE_SPECIFIER = ""; STRIP_STYLE = "non-global"; @@ -567,7 +567,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.macos; + PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.appflowy.flutter; PRODUCT_NAME = AppFlowy; PROVISIONING_PROFILE_SPECIFIER = ""; STRIP_STYLE = "non-global"; @@ -593,7 +593,7 @@ "@executable_path/../Frameworks", ); ONLY_ACTIVE_ARCH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.macos; + PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.appflowy.flutter; PRODUCT_NAME = AppFlowy; PROVISIONING_PROFILE_SPECIFIER = ""; STRIP_STYLE = "non-global"; diff --git a/frontend/appflowy_flutter/macos/Runner/DebugProfile.entitlements b/frontend/appflowy_flutter/macos/Runner/DebugProfile.entitlements index a816d4e6c6..ae544967cf 100644 --- a/frontend/appflowy_flutter/macos/Runner/DebugProfile.entitlements +++ b/frontend/appflowy_flutter/macos/Runner/DebugProfile.entitlements @@ -2,19 +2,11 @@ + com.apple.security.cs.allow-jit + com.apple.security.temporary-exception.files.absolute-path.read-write / - com.apple.security.files.user-selected.read-write - - com.apple.security.app-sandbox - - com.apple.security.cs.allow-jit - - com.apple.security.network.server - - com.apple.security.network.client - diff --git a/frontend/appflowy_flutter/macos/Runner/Info.plist b/frontend/appflowy_flutter/macos/Runner/Info.plist index 6c91d53468..43954508aa 100644 --- a/frontend/appflowy_flutter/macos/Runner/Info.plist +++ b/frontend/appflowy_flutter/macos/Runner/Info.plist @@ -25,6 +25,17 @@ APPL CFBundleShortVersionString $(FLUTTER_BUILD_NAME) + CFBundleURLTypes + + + CFBundleURLName + + CFBundleURLSchemes + + io.appflowy.appflowy-flutter + + + CFBundleVersion $(FLUTTER_BUILD_NUMBER) LSMinimumSystemVersion @@ -40,16 +51,5 @@ MainMenu NSPrincipalClass NSApplication - CFBundleURLTypes - - - CFBundleURLName - - CFBundleURLSchemes - - io.appflowy.appflowy-flutter - - - diff --git a/frontend/appflowy_flutter/macos/Runner/Release.entitlements b/frontend/appflowy_flutter/macos/Runner/Release.entitlements index 7717cae209..f1e49b4f10 100644 --- a/frontend/appflowy_flutter/macos/Runner/Release.entitlements +++ b/frontend/appflowy_flutter/macos/Runner/Release.entitlements @@ -6,11 +6,5 @@ / - com.apple.security.files.user-selected.read-write - - com.apple.security.app-sandbox - - com.apple.security.network.client - diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/button.dart b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/button.dart index ae5e4007a3..65327d52df 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/button.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/button.dart @@ -121,6 +121,8 @@ class FlowyTextButton extends StatelessWidget { final TextDecoration? decoration; + final String? fontFamily; + // final HoverDisplayConfig? hoverDisplay; const FlowyTextButton( this.text, { @@ -139,6 +141,7 @@ class FlowyTextButton extends StatelessWidget { this.tooltip, this.constraints = const BoxConstraints(minWidth: 58.0, minHeight: 30.0), this.decoration, + this.fontFamily, }) : super(key: key); @override @@ -157,6 +160,7 @@ class FlowyTextButton extends StatelessWidget { color: fontColor, textAlign: TextAlign.center, decoration: decoration, + fontFamily: fontFamily, ), ); diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/color_picker.dart b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/color_picker.dart index 246c45c7ae..fb5bfe1610 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/color_picker.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/color_picker.dart @@ -2,8 +2,8 @@ import 'package:flowy_infra/image.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; -class ColorOption { - const ColorOption({ +class FlowyColorOption { + const FlowyColorOption({ required this.color, required this.name, }); @@ -13,12 +13,13 @@ class ColorOption { } class FlowyColorPicker extends StatelessWidget { - final List colors; + final List colors; final Color? selected; final Function(Color color, int index)? onTap; final double separatorSize; final double iconSize; final double itemHeight; + final Border? border; const FlowyColorPicker({ Key? key, @@ -28,6 +29,7 @@ class FlowyColorPicker extends StatelessWidget { this.separatorSize = 4, this.iconSize = 16, this.itemHeight = 32, + this.border, }) : super(key: key); @override @@ -46,7 +48,10 @@ class FlowyColorPicker extends StatelessWidget { ); } - Widget _buildColorOption(ColorOption option, int i) { + Widget _buildColorOption( + FlowyColorOption option, + int i, + ) { Widget? checkmark; if (selected == option.color) { checkmark = svgWidget("grid/checkmark"); @@ -55,11 +60,11 @@ class FlowyColorPicker extends StatelessWidget { final colorIcon = SizedBox.square( dimension: iconSize, child: Container( - decoration: BoxDecoration( - color: option.color, - shape: BoxShape.circle, - ), - ), + decoration: BoxDecoration( + color: option.color, + shape: BoxShape.circle, + border: border, + )), ); return SizedBox( diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/text.dart b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/text.dart index 972a330ef2..af317d53f5 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/text.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/text.dart @@ -10,6 +10,7 @@ class FlowyText extends StatelessWidget { final Color? color; final TextDecoration? decoration; final bool selectable; + final String? fontFamily; const FlowyText( this.title, { @@ -22,6 +23,7 @@ class FlowyText extends StatelessWidget { this.maxLines = 1, this.decoration, this.selectable = false, + this.fontFamily, }) : super(key: key); const FlowyText.regular( @@ -34,6 +36,7 @@ class FlowyText extends StatelessWidget { this.maxLines = 1, this.decoration, this.selectable = false, + this.fontFamily, }) : fontWeight = FontWeight.w400, super(key: key); @@ -47,6 +50,7 @@ class FlowyText extends StatelessWidget { this.maxLines = 1, this.decoration, this.selectable = false, + this.fontFamily, }) : fontWeight = FontWeight.w500, super(key: key); @@ -60,6 +64,7 @@ class FlowyText extends StatelessWidget { this.maxLines = 1, this.decoration, this.selectable = false, + this.fontFamily, }) : fontWeight = FontWeight.w600, super(key: key); @@ -78,6 +83,7 @@ class FlowyText extends StatelessWidget { fontWeight: fontWeight, color: color, decoration: decoration, + fontFamily: fontFamily, ), ); } else { @@ -91,6 +97,7 @@ class FlowyText extends StatelessWidget { fontWeight: fontWeight, color: color, decoration: decoration, + fontFamily: fontFamily, ), ); } diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index 3b0266b0cd..6aa405c9fa 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -53,10 +53,10 @@ packages: dependency: "direct main" description: name: appflowy_editor - sha256: "3561bd7bd99541508353034130a98ab2d9be54f690bb982f85c2b3eedb8fe63e" + sha256: "5fa08d19842b402482aff5044b25d164dbef3eb6155015d123ef02925ba5b52d" url: "https://pub.dev" source: hosted - version: "1.0.0-dev.1" + version: "1.0.0-dev.3" appflowy_popover: dependency: "direct main" description: @@ -1605,6 +1605,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + visibility_detector: + dependency: transitive + description: + name: visibility_detector + sha256: dd5cc11e13494f432d15939c3aa8ae76844c42b723398643ce9addb88a5ed420 + url: "https://pub.dev" + source: hosted + version: "0.4.0+2" vm_service: dependency: transitive description: diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index 0e5daf54ad..85d2496de1 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -42,7 +42,7 @@ dependencies: git: url: https://github.com/AppFlowy-IO/appflowy-board.git ref: a183c57 - appflowy_editor: 1.0.0-dev.1 + appflowy_editor: 1.0.0-dev.3 appflowy_popover: path: packages/appflowy_popover @@ -163,6 +163,7 @@ flutter: assets: - assets/images/ - assets/images/home/ + - assets/images/editor/align/ - assets/images/editor/ - assets/images/grid/ - assets/images/emoji/ diff --git a/frontend/rust-lib/flowy-database2/src/manager.rs b/frontend/rust-lib/flowy-database2/src/manager.rs index c6ccc25901..4e028fa251 100644 --- a/frontend/rust-lib/flowy-database2/src/manager.rs +++ b/frontend/rust-lib/flowy-database2/src/manager.rs @@ -84,6 +84,7 @@ impl DatabaseManager2 { self.get_database(&database_id).await } + #[tracing::instrument(level = "debug", skip(self), err)] pub async fn get_database_id_with_view_id(&self, view_id: &str) -> FlowyResult { let database_id = self.with_user_database(Err(FlowyError::internal()), |database| { database @@ -98,6 +99,7 @@ impl DatabaseManager2 { return Ok(editor.clone()); } + tracing::trace!("create new editor for database {}", database_id); let mut editors = self.editors.write().await; let database = MutexDatabase::new(self.with_user_database( Err(FlowyError::record_not_found()), @@ -178,6 +180,7 @@ impl DatabaseManager2 { Ok(()) } + #[tracing::instrument(level = "trace", skip(self), err)] pub async fn create_linked_view( &self, name: String, @@ -188,11 +191,8 @@ impl DatabaseManager2 { self.with_user_database( Err(FlowyError::internal().context("Create database view failed")), |user_database| { - let database = user_database - .get_database(&database_id) - .ok_or_else(FlowyError::record_not_found)?; let params = CreateViewParams::new(database_id, database_view_id, name, layout.into()); - database.create_linked_view(params)?; + user_database.create_database_linked_view(params)?; Ok(()) }, )?; diff --git a/frontend/rust-lib/flowy-document2/src/manager.rs b/frontend/rust-lib/flowy-document2/src/manager.rs index 518adbc2cf..ddac87b5d1 100644 --- a/frontend/rust-lib/flowy-document2/src/manager.rs +++ b/frontend/rust-lib/flowy-document2/src/manager.rs @@ -55,7 +55,6 @@ impl DocumentManager { } pub fn open_document(&self, doc_id: String) -> FlowyResult> { - tracing::debug!("open a document: {:?}", &doc_id); if let Some(doc) = self.documents.read().get(&doc_id) { return Ok(doc.clone()); } diff --git a/frontend/rust-lib/flowy-folder2/src/manager.rs b/frontend/rust-lib/flowy-folder2/src/manager.rs index 4f14031c4d..0d1e43d8ce 100644 --- a/frontend/rust-lib/flowy-folder2/src/manager.rs +++ b/frontend/rust-lib/flowy-folder2/src/manager.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::HashSet; use std::ops::Deref; use std::sync::{Arc, Weak}; @@ -203,27 +203,26 @@ impl Folder2Manager { let handler = self.get_handler(&view_layout)?; let user_id = self.user.user_id()?; let meta = params.meta.clone(); - match params.initial_data.is_empty() { - true => { - tracing::trace!("Create view with build-in data"); - handler - .create_built_in_view(user_id, ¶ms.view_id, ¶ms.name, view_layout.clone()) - .await?; - }, - false => { - tracing::trace!("Create view with view data"); - handler - .create_view_with_view_data( - user_id, - ¶ms.view_id, - ¶ms.name, - params.initial_data.clone(), - view_layout.clone(), - meta, - ) - .await?; - }, + + if meta.is_empty() && params.initial_data.is_empty() { + tracing::trace!("Create view with build-in data"); + handler + .create_built_in_view(user_id, ¶ms.view_id, ¶ms.name, view_layout.clone()) + .await?; + } else { + tracing::trace!("Create view with view data"); + handler + .create_view_with_view_data( + user_id, + ¶ms.view_id, + ¶ms.name, + params.initial_data.clone(), + view_layout.clone(), + meta, + ) + .await?; } + let view = create_view(params, view_layout); self.with_folder((), |folder| { folder.insert_view(view.clone()); @@ -245,28 +244,6 @@ impl Folder2Manager { Ok(()) } - pub async fn create_view_with_data( - &self, - view_id: &str, - name: &str, - view_layout: ViewLayout, - data: Vec, - ) -> FlowyResult<()> { - let user_id = self.user.user_id()?; - let handler = self.get_handler(&view_layout)?; - handler - .create_view_with_view_data( - user_id, - view_id, - name, - data, - view_layout, - HashMap::default(), - ) - .await?; - Ok(()) - } - /// Returns the view with the given view id. /// The child views of the view will only access the first. So if you want to get the child view's /// child view, you need to call this method again.