Browse Source

chore: bump version 0.3.0 (#3252)

Lucas.Xu 1 năm trước cách đây
mục cha
commit
a0481e8020

+ 16 - 0
CHANGELOG.md

@@ -1,5 +1,21 @@
 # Release Notes
 
+## Version 0.3.0 - 08/22/2023
+
+### New Features
+
+- Improve paste features:
+  - Paste HTML content from website.
+  - Paste image from clipboard.
+
+- Support Group by Date in Kanban Board.
+- Notarize the macOS package, which is now verified by Apple.
+- Add Persian language translations.
+
+### Bug fixes
+
+- Some UI issues
+
 ## Version 0.2.9 - 08/08/2023
 
 ### New Features

+ 1 - 1
frontend/Makefile.toml

@@ -24,7 +24,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
 CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
 CARGO_MAKE_CRATE_NAME = "dart-ffi"
 LIB_NAME = "dart_ffi"
-CURRENT_APP_VERSION = "0.2.9"
+CURRENT_APP_VERSION = "0.3.0"
 FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite"
 PRODUCT_NAME = "AppFlowy"
 # CRATE_TYPE: https://doc.rust-lang.org/reference/linkage.html

+ 1 - 1
frontend/appflowy_flutter/integration_test/document/document_copy_and_paste_test.dart

@@ -245,7 +245,7 @@ extension on WidgetTester {
     await beforeTest?.call(editor.getCurrentEditorState());
 
     // mock the clipboard
-    getIt<ClipboardService>().setData(
+    await getIt<ClipboardService>().setData(
       ClipboardServiceData(
         plainText: plainText,
         html: html,

+ 1 - 0
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart

@@ -52,6 +52,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
     ...codeBlockCommands,
     customCopyCommand,
     customPasteCommand,
+    customCutCommand,
     ...standardCommandShortcutEvents,
   ];
 

+ 1 - 0
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart

@@ -22,6 +22,7 @@ final inAppJsonFormat = CustomValueFormat<String>(
     }
     return null;
   },
+  onEncode: (value, platformType) => utf8.encode(value),
 );
 
 class ClipboardServiceData {

+ 24 - 0
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/copy_and_paste/custom_cut_command.dart

@@ -0,0 +1,24 @@
+import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/editor_state_paste_node_extension.dart';
+import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
+import 'package:appflowy_editor/appflowy_editor.dart';
+import 'package:flutter/material.dart';
+
+/// cut.
+///
+/// - support
+///   - desktop
+///   - web
+///   - mobile
+///
+final CommandShortcutEvent customCutCommand = CommandShortcutEvent(
+  key: 'cut the selected content',
+  command: 'ctrl+x',
+  macOSCommand: 'cmd+x',
+  handler: _cutCommandHandler,
+);
+
+CommandShortcutEventHandler _cutCommandHandler = (editorState) {
+  customCopyCommand.execute(editorState);
+  editorState.deleteSelectionIfNeeded();
+  return KeyEventResult.handled;
+};

+ 40 - 17
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/copy_and_paste/editor_state_paste_node_extension.dart

@@ -14,25 +14,29 @@ extension PasteNodes on EditorState {
     final transaction = this.transaction;
     final insertedDelta = insertedNode.delta;
     // if the node is empty, replace it with the inserted node.
-    if (delta.isEmpty || insertedDelta == null) {
+    if (delta.isEmpty) {
       transaction.insertNode(
         selection.end.path.next,
-        node.copyWith(
-          type: node.type,
-          attributes: {
-            ...node.attributes,
-            ...insertedNode.attributes,
-          },
-        ),
+        insertedDelta == null
+            ? node.copyWith(
+                type: node.type,
+                attributes: {
+                  ...node.attributes,
+                  ...insertedNode.attributes,
+                },
+              )
+            : insertedNode,
       );
       transaction.deleteNode(node);
+      final path = calculatePath(selection.end.path, [insertedNode]);
+      final offset = calculateLength([insertedNode]);
       transaction.afterSelection = Selection.collapsed(
         Position(
-          path: selection.end.path,
-          offset: insertedDelta?.length ?? 0,
+          path: path,
+          offset: offset,
         ),
       );
-    } else {
+    } else if (insertedDelta != null) {
       // if the node is not empty, insert the delta from inserted node after the selection.
       transaction.insertTextDelta(node, selection.endIndex, insertedDelta);
     }
@@ -53,7 +57,7 @@ extension PasteNodes on EditorState {
     }
     final transaction = this.transaction;
 
-    final lastNodeLength = nodes.last.delta?.length ?? 0;
+    final lastNodeLength = calculateLength(nodes);
     // merge the current selected node delta into the nodes.
     if (delta.isNotEmpty) {
       nodes.first.insertDelta(
@@ -86,13 +90,10 @@ extension PasteNodes on EditorState {
     // delete the current node.
     transaction.deleteNode(node);
 
-    var path = selection.end.path;
-    for (var i = 0; i < nodes.length; i++) {
-      path = path.next;
-    }
+    final path = calculatePath(selection.start.path, nodes);
     transaction.afterSelection = Selection.collapsed(
       Position(
-        path: path.previous, // because a node is deleted.
+        path: path,
         offset: lastNodeLength,
       ),
     );
@@ -116,6 +117,28 @@ extension PasteNodes on EditorState {
     assert(this.selection?.isCollapsed == true);
     return this.selection;
   }
+
+  Path calculatePath(Path start, List<Node> nodes) {
+    var path = start;
+    for (var i = 0; i < nodes.length; i++) {
+      path = path.next;
+    }
+    path = path.previous;
+    if (nodes.last.children.isNotEmpty) {
+      return [
+        ...path,
+        ...calculatePath([0], nodes.last.children.toList())
+      ];
+    }
+    return path;
+  }
+
+  int calculateLength(List<Node> nodes) {
+    if (nodes.last.children.isNotEmpty) {
+      return calculateLength(nodes.last.children.toList());
+    }
+    return nodes.last.delta?.length ?? 0;
+  }
 }
 
 extension on Node {

+ 1 - 0
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart

@@ -4,6 +4,7 @@ export 'callout/callout_block_component.dart';
 export 'code_block/code_block_component.dart';
 export 'code_block/code_block_shortcut_event.dart';
 export 'copy_and_paste/custom_copy_command.dart';
+export 'copy_and_paste/custom_cut_command.dart';
 export 'copy_and_paste/custom_paste_command.dart';
 export 'database/database_view_block_component.dart';
 export 'database/inline_database_menu_item.dart';

+ 12 - 9
frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_user_view.dart

@@ -1,5 +1,5 @@
-import 'dart:convert';
 import 'dart:async';
+import 'dart:convert';
 
 import 'package:appflowy/env/env.dart';
 import 'package:appflowy/generated/flowy_svgs.g.dart';
@@ -65,7 +65,7 @@ class SettingsUserView extends StatelessWidget {
     );
   }
 
-  /// Renders either a login or logout button based on the user's authentication status.
+  /// Renders either a login or logout button based on the user's authentication status, or nothing if Supabase is not enabled.
   ///
   /// This function checks the current user's authentication type and Supabase
   /// configuration to determine whether to render a third-party login button
@@ -74,14 +74,17 @@ class SettingsUserView extends StatelessWidget {
     BuildContext context,
     SettingsUserState state,
   ) {
-    if (isSupabaseEnabled) {
-      // If the user is logged in locally, render a third-party login button.
-      if (state.userProfile.authType == AuthTypePB.Local) {
-        return SettingThirdPartyLogin(
-          didLogin: didLogin,
-        );
-      }
+    if (!isSupabaseEnabled) {
+      return const SizedBox.shrink();
     }
+
+    // If the user is logged in locally, render a third-party login button.
+    if (state.userProfile.authType == AuthTypePB.Local) {
+      return SettingThirdPartyLogin(
+        didLogin: didLogin,
+      );
+    }
+
     return SettingLogoutButton(user: user, didLogout: didLogout);
   }
 

+ 6 - 6
frontend/appflowy_flutter/pubspec.lock

@@ -54,8 +54,8 @@ packages:
     dependency: "direct main"
     description:
       path: "."
-      ref: "56474e8b"
-      resolved-ref: "56474e8bd9f08be7090495c255eea20a694ab1f3"
+      ref: f4db21c
+      resolved-ref: f4db21c3678290d133ae6cffa015d3d79920bf84
       url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
     source: git
     version: "1.2.3"
@@ -1451,18 +1451,18 @@ packages:
     dependency: "direct main"
     description:
       name: super_clipboard
-      sha256: "204284b1a721d33a65bcab077b191a3b7379b46a231f05688d17220153338ede"
+      sha256: ba484eb42ce621b69241d18d2a8494109589e8796eb6a3399e6c8f9cdaab8303
       url: "https://pub.dev"
     source: hosted
-    version: "0.6.0"
+    version: "0.6.3"
   super_native_extensions:
     dependency: transitive
     description:
       name: super_native_extensions
-      sha256: "1f15e9b1adb0bc59cf9b889a0b248f3c192fa17e2d5c923aeeec6d4fa2eeffd6"
+      sha256: dfe0a1c74430db946be973878da3f8611b8f124137f1dcbdf883e4605db40bd8
       url: "https://pub.dev"
     source: hosted
-    version: "0.6.0"
+    version: "0.6.3"
   sync_http:
     dependency: transitive
     description:

+ 3 - 3
frontend/appflowy_flutter/pubspec.yaml

@@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
 # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
 # Read more about iOS versioning at
 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
-version: 0.2.9
+version: 0.3.0
 
 environment:
   sdk: ">=3.0.0 <4.0.0"
@@ -48,7 +48,7 @@ dependencies:
   appflowy_editor:
     git:
       url: https://github.com/AppFlowy-IO/appflowy-editor.git
-      ref: 56474e8b
+      ref: f4db21c
   appflowy_popover:
     path: packages/appflowy_popover
 
@@ -107,7 +107,7 @@ dependencies:
   url_protocol:
   hive: ^2.2.3
   hive_flutter: ^1.1.0
-  super_clipboard: ^0.6.0
+  super_clipboard: ^0.6.3
 
 dev_dependencies:
   flutter_lints: ^2.0.1