Просмотр исходного кода

feat: add error tips when loading image fail

Lucas.Xu 2 лет назад
Родитель
Сommit
2c37bf806e

+ 8 - 7
frontend/app_flowy/packages/appflowy_editor/example/assets/example.json

@@ -59,13 +59,6 @@
           }
         ]
       },
-      {
-        "type": "image",
-        "attributes": {
-          "image_src": "https://s1.ax1x.com/2022/08/24/vgAJED.png",
-          "align": "center"
-        }
-      },
       {
         "type": "text",
         "delta": [
@@ -129,6 +122,14 @@
           "heading": "h3"
         }
       },
+      {
+        "type": "image",
+        "attributes": {
+          "image_src": "https://s1.ax1x.com/2022/08/24/vgAJED.png",
+          "align": "left",
+          "width": 300
+        }
+      },
       {
         "type": "text",
         "delta": [

+ 4 - 1
frontend/app_flowy/packages/appflowy_editor/lib/src/render/image/image_node_builder.dart

@@ -11,7 +11,10 @@ class ImageNodeBuilder extends NodeWidgetBuilder<Node> {
   Widget build(NodeWidgetContext<Node> context) {
     final src = context.node.attributes['image_src'];
     final align = context.node.attributes['align'];
-    final width = context.node.attributes['width'];
+    double? width;
+    if (context.node.attributes.containsKey('width')) {
+      width = context.node.attributes['width'].toDouble();
+    }
     return ImageNodeWidget(
       key: context.node.key,
       src: src,

+ 27 - 3
frontend/app_flowy/packages/appflowy_editor/lib/src/render/image/image_node_widget.dart

@@ -1,4 +1,5 @@
 import 'package:appflowy_editor/src/infra/flowy_svg.dart';
+import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart';
 import 'package:flutter/material.dart';
 
 class ImageNodeWidget extends StatefulWidget {
@@ -55,7 +56,12 @@ class _ImageNodeWidgetState extends State<ImageNodeWidget> {
   @override
   Widget build(BuildContext context) {
     // only support network image.
-    return _buildNetworkImage(context);
+
+    return Container(
+      width: defaultMaxTextNodeWidth,
+      padding: const EdgeInsets.only(top: 8, bottom: 8),
+      child: _buildNetworkImage(context),
+    );
   }
 
   Widget _buildNetworkImage(BuildContext context) {
@@ -77,8 +83,13 @@ class _ImageNodeWidgetState extends State<ImageNodeWidget> {
     final networkImage = Image.network(
       widget.src,
       width: _imageWidth == null ? null : _imageWidth! - _distance,
+      gaplessPlayback: true,
       loadingBuilder: (context, child, loadingProgress) =>
           loadingProgress == null ? child : _buildLoading(context),
+      errorBuilder: (context, error, stackTrace) {
+        _imageWidth ??= defaultMaxTextNodeWidth;
+        return _buildError(context);
+      },
     );
     if (_imageWidth == null) {
       _imageStream = networkImage.image.resolve(const ImageConfiguration())
@@ -127,8 +138,7 @@ class _ImageNodeWidgetState extends State<ImageNodeWidget> {
 
   Widget _buildLoading(BuildContext context) {
     return SizedBox(
-      width: _imageWidth,
-      height: 300,
+      height: 150,
       child: Row(
         mainAxisAlignment: MainAxisAlignment.center,
         children: [
@@ -145,6 +155,20 @@ class _ImageNodeWidgetState extends State<ImageNodeWidget> {
     );
   }
 
+  Widget _buildError(BuildContext context) {
+    return Container(
+      height: 100,
+      width: _imageWidth,
+      alignment: Alignment.center,
+      padding: const EdgeInsets.only(top: 8.0, bottom: 8.0),
+      decoration: BoxDecoration(
+        borderRadius: const BorderRadius.all(Radius.circular(4.0)),
+        border: Border.all(width: 1, color: Colors.black),
+      ),
+      child: const Text('Could not load the image'),
+    );
+  }
+
   Widget _buildEdgeGesture(
     BuildContext context, {
     double? top,

+ 10 - 2
frontend/app_flowy/packages/appflowy_editor/lib/src/render/image/image_upload_widget.dart

@@ -8,6 +8,7 @@ import 'package:appflowy_editor/src/render/selection_menu/selection_menu_service
 import 'package:flutter/material.dart';
 
 OverlayEntry? _imageUploadMenu;
+EditorState? _editorState;
 void showImageUploadMenu(
   EditorState editorState,
   SelectionMenuService menuService,
@@ -23,11 +24,11 @@ void showImageUploadMenu(
       child: Material(
         child: ImageUploadMenu(
           onSubmitted: (text) {
-            _dismissImageUploadMenu();
+            // _dismissImageUploadMenu();
             editorState.insertImageNode(text);
           },
           onUpload: (text) {
-            _dismissImageUploadMenu();
+            // _dismissImageUploadMenu();
             editorState.insertImageNode(text);
           },
         ),
@@ -36,11 +37,18 @@ void showImageUploadMenu(
   });
 
   Overlay.of(context)?.insert(_imageUploadMenu!);
+
+  editorState.service.selectionService.currentSelection
+      .addListener(_dismissImageUploadMenu);
 }
 
 void _dismissImageUploadMenu() {
   _imageUploadMenu?.remove();
   _imageUploadMenu = null;
+
+  _editorState?.service.selectionService.currentSelection
+      .removeListener(_dismissImageUploadMenu);
+  _editorState = null;
 }
 
 class ImageUploadMenu extends StatefulWidget {

+ 4 - 2
frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_widget.dart

@@ -205,8 +205,10 @@ class _SelectionMenuWidgetState extends State<SelectionMenuWidget> {
     if (event.logicalKey == LogicalKeyboardKey.enter) {
       if (0 <= _selectedIndex && _selectedIndex < _showingItems.length) {
         _deleteLastCharacters(length: keyword.length + 1);
-        _showingItems[_selectedIndex]
-            .handler(widget.editorState, widget.menuService, context);
+        WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
+          _showingItems[_selectedIndex]
+              .handler(widget.editorState, widget.menuService, context);
+        });
         return KeyEventResult.handled;
       }
     } else if (event.logicalKey == LogicalKeyboardKey.escape) {

+ 5 - 3
frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/delete_text_handler.dart

@@ -34,9 +34,9 @@ KeyEventResult _handleBackspace(EditorState editorState, RawKeyEvent event) {
       } else {
         // 2. non-style
         // find previous text node.
-        while (textNode.previous != null) {
-          if (textNode.previous is TextNode) {
-            final previous = textNode.previous as TextNode;
+        var previous = textNode.previous;
+        while (previous != null) {
+          if (previous is TextNode) {
             transactionBuilder
               ..mergeText(previous, textNode)
               ..deleteNode(textNode)
@@ -47,6 +47,8 @@ KeyEventResult _handleBackspace(EditorState editorState, RawKeyEvent event) {
                 ),
               );
             break;
+          } else {
+            previous = previous.previous;
           }
         }
       }