From eb508a3ec9955e564452c970592d96d90d49d27f Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 4 Feb 2025 14:05:57 +0800 Subject: [PATCH] fix: editor stuck on image loading loop when uploading image in row document (#7313) * fix: editor stuck on image loading loop when uploading image in row document * test: editor stuck on image loading loop when uploading image in row document --- .../desktop/cloud/cloud_runner.dart | 4 + .../cloud/database/database_image_test.dart | 80 +++++++++++++++++++ .../cloud/database/database_test_runner.dart | 9 +++ .../image/resizeable_image.dart | 11 ++- 4 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 frontend/appflowy_flutter/integration_test/desktop/cloud/database/database_image_test.dart create mode 100644 frontend/appflowy_flutter/integration_test/desktop/cloud/database/database_test_runner.dart diff --git a/frontend/appflowy_flutter/integration_test/desktop/cloud/cloud_runner.dart b/frontend/appflowy_flutter/integration_test/desktop/cloud/cloud_runner.dart index bc994eba99..a8c05d5f80 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/cloud/cloud_runner.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/cloud/cloud_runner.dart @@ -1,5 +1,6 @@ import 'data_migration/data_migration_test_runner.dart' as data_migration_test_runner; +import 'database/database_test_runner.dart' as database_test_runner; import 'document/document_test_runner.dart' as document_test_runner; import 'set_env.dart' as preset_af_cloud_env_test; import 'sidebar/sidebar_icon_test.dart' as sidebar_icon_test; @@ -28,4 +29,7 @@ Future main() async { sidebar_move_page_test.main(); sidebar_rename_untitled_test.main(); sidebar_icon_test.main(); + + // database + database_test_runner.main(); } diff --git a/frontend/appflowy_flutter/integration_test/desktop/cloud/database/database_image_test.dart b/frontend/appflowy_flutter/integration_test/desktop/cloud/database/database_image_test.dart new file mode 100644 index 0000000000..5561d40033 --- /dev/null +++ b/frontend/appflowy_flutter/integration_test/desktop/cloud/database/database_image_test.dart @@ -0,0 +1,80 @@ +import 'dart:io'; + +import 'package:appflowy/core/config/kv.dart'; +import 'package:appflowy/core/config/kv_keys.dart'; +import 'package:appflowy/env/cloud_env.dart'; +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/image/resizeable_image.dart'; +import 'package:appflowy/startup/startup.dart'; +import 'package:appflowy_backend/protobuf/flowy-folder/view.pbenum.dart'; +import 'package:appflowy_editor/appflowy_editor.dart' + hide UploadImageMenu, ResizableImage; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:path/path.dart' as p; +import 'package:path_provider/path_provider.dart'; + +import '../../../shared/constants.dart'; +import '../../../shared/database_test_op.dart'; +import '../../../shared/mock/mock_file_picker.dart'; +import '../../../shared/util.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + // copy link to block + group('database image:', () { + testWidgets('insert image', (tester) async { + await tester.initializeAppFlowy( + cloudType: AuthenticatorType.appflowyCloudSelfHost, + ); + await tester.tapGoogleLoginInButton(); + await tester.expectToSeeHomePageWithGetStartedPage(); + + // open the first row detail page and upload an image + await tester.createNewPageInSpace( + spaceName: Constants.generalSpaceName, + layout: ViewLayoutPB.Grid, + pageName: 'database image', + ); + await tester.openFirstRowDetailPage(); + + // insert an image block + { + await tester.editor.tapLineOfEditorAt(0); + await tester.editor.showSlashMenu(); + await tester.editor.tapSlashMenuItemWithName( + LocaleKeys.document_slashMenu_name_image.tr(), + ); + } + + // upload an image + { + final image = await rootBundle.load('assets/test/images/sample.jpeg'); + final tempDirectory = await getTemporaryDirectory(); + final imagePath = p.join(tempDirectory.path, 'sample.jpeg'); + final file = File(imagePath) + ..writeAsBytesSync(image.buffer.asUint8List()); + + mockPickFilePaths( + paths: [imagePath], + ); + + await getIt().set(KVKeys.kCloudType, '0'); + await tester.tapButtonWithName( + LocaleKeys.document_imageBlock_upload_placeholder.tr(), + ); + await tester.pumpAndSettle(); + expect(find.byType(ResizableImage), findsOneWidget); + final node = tester.editor.getCurrentEditorState().getNodeAtPath([0])!; + expect(node.type, ImageBlockKeys.type); + expect(node.attributes[ImageBlockKeys.url], isNotEmpty); + + // remove the temp file + file.deleteSync(); + } + }); + }); +} diff --git a/frontend/appflowy_flutter/integration_test/desktop/cloud/database/database_test_runner.dart b/frontend/appflowy_flutter/integration_test/desktop/cloud/database/database_test_runner.dart new file mode 100644 index 0000000000..4d1a623f07 --- /dev/null +++ b/frontend/appflowy_flutter/integration_test/desktop/cloud/database/database_test_runner.dart @@ -0,0 +1,9 @@ +import 'package:integration_test/integration_test.dart'; + +import 'database_image_test.dart' as database_image_test; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + database_image_test.main(); +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/resizeable_image.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/resizeable_image.dart index 021476aa4e..99524b0d71 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/resizeable_image.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/resizeable_image.dart @@ -70,8 +70,12 @@ class _ResizableImageState extends State { @override void initState() { super.initState(); + imageWidth = widget.width; - _userProfilePB = context.read()?.userProfile; + + // read the user profile from the user workspace bloc or the document bloc + _userProfilePB = context.read()?.userProfile ?? + context.read().state.userProfilePB; } @override @@ -97,11 +101,6 @@ class _ResizableImageState extends State { Widget child; final src = widget.src; if (isURL(src)) { - // load network image - if (widget.type == CustomImageType.internal && _userProfilePB == null) { - return _buildLoading(context); - } - _cacheImage = FlowyNetworkImage( url: widget.src, width: imageWidth - moveDistance,