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.