Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: retry load image #7179

Merged
merged 7 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -591,10 +591,14 @@ CustomImageBlockComponentBuilder _buildCustomImageBlockComponentBuilder(
return CustomImageBlockComponentBuilder(
configuration: configuration,
showMenu: true,
menuBuilder: (node, state) => Positioned(
menuBuilder: (node, state, imageStateNotifier) => Positioned(
top: 10,
right: 10,
child: ImageMenu(node: node, state: state),
child: ImageMenu(
node: node,
state: state,
imageStateNotifier: imageStateNotifier,
),
),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ Node customImageNode({
typedef CustomImageBlockComponentMenuBuilder = Widget Function(
Node node,
CustomImageBlockComponentState state,
ValueNotifier<ResizableImageState> imageStateNotifier,
);

class CustomImageBlockComponentBuilder extends BlockComponentBuilder {
Expand Down Expand Up @@ -149,6 +150,8 @@ class CustomImageBlockComponentState extends State<CustomImageBlockComponent>
late final editorState = Provider.of<EditorState>(context, listen: false);

final showActionsNotifier = ValueNotifier<bool>(false);
final imageStateNotifier =
ValueNotifier<ResizableImageState>(ResizableImageState.loading);

bool alwaysShowMenu = false;

Expand Down Expand Up @@ -185,6 +188,7 @@ class CustomImageBlockComponentState extends State<CustomImageBlockComponent>
editable: editorState.editable,
alignment: alignment,
type: imageType,
onStateChange: (state) => imageStateNotifier.value = state,
onDoubleTap: () => showDialog(
context: context,
builder: (_) => InteractiveImageViewer(
Expand Down Expand Up @@ -260,7 +264,7 @@ class CustomImageBlockComponentState extends State<CustomImageBlockComponent>
child: child!,
),
if (value && url.isNotEmpty == true)
widget.menuBuilder!(widget.node, this),
widget.menuBuilder!(widget.node, this, imageStateNotifier),
],
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/block_menu/block_menu_button.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/common.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/resizeable_image.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart';
Expand All @@ -25,10 +26,12 @@ class ImageMenu extends StatefulWidget {
super.key,
required this.node,
required this.state,
required this.imageStateNotifier,
});

final Node node;
final CustomImageBlockComponentState state;
final ValueNotifier<ResizableImageState> imageStateNotifier;

@override
State<ImageMenu> createState() => _ImageMenuState();
Expand All @@ -40,46 +43,55 @@ class _ImageMenuState extends State<ImageMenu> {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Container(
height: 32,
decoration: BoxDecoration(
color: theme.cardColor,
boxShadow: [
BoxShadow(
blurRadius: 5,
spreadRadius: 1,
color: Colors.black.withOpacity(0.1),
),
],
borderRadius: BorderRadius.circular(4.0),
),
child: Row(
children: [
const HSpace(4),
MenuBlockButton(
tooltip: LocaleKeys.document_imageBlock_openFullScreen.tr(),
iconData: FlowySvgs.full_view_s,
onTap: openFullScreen,
return ValueListenableBuilder<ResizableImageState>(
valueListenable: widget.imageStateNotifier,
builder: (_, state, child) {
if (state == ResizableImageState.loading) {
return const SizedBox.shrink();
}

return Container(
height: 32,
decoration: BoxDecoration(
color: theme.cardColor,
boxShadow: [
BoxShadow(
blurRadius: 5,
spreadRadius: 1,
color: Colors.black.withOpacity(0.1),
),
],
borderRadius: BorderRadius.circular(4.0),
),
const HSpace(4),
MenuBlockButton(
tooltip: LocaleKeys.editor_copy.tr(),
iconData: FlowySvgs.copy_s,
onTap: copyImageLink,
child: Row(
children: [
const HSpace(4),
MenuBlockButton(
tooltip: LocaleKeys.document_imageBlock_openFullScreen.tr(),
iconData: FlowySvgs.full_view_s,
onTap: openFullScreen,
),
const HSpace(4),
MenuBlockButton(
tooltip: LocaleKeys.editor_copy.tr(),
iconData: FlowySvgs.copy_s,
onTap: copyImageLink,
),
const HSpace(4),
if (widget.state.editorState.editable) ...[
_ImageAlignButton(node: widget.node, state: widget.state),
const _Divider(),
MenuBlockButton(
tooltip: LocaleKeys.button_delete.tr(),
iconData: FlowySvgs.trash_s,
onTap: deleteImage,
),
const HSpace(4),
],
],
),
const HSpace(4),
if (widget.state.editorState.editable) ...[
_ImageAlignButton(node: widget.node, state: widget.state),
const _Divider(),
MenuBlockButton(
tooltip: LocaleKeys.button_delete.tr(),
iconData: FlowySvgs.trash_s,
onTap: deleteImage,
),
const HSpace(4),
],
],
),
);
},
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:string_validator/string_validator.dart';

enum ResizableImageState {
loading,
loaded,
failed,
}

class ResizableImage extends StatefulWidget {
const ResizableImage({
super.key,
Expand All @@ -25,6 +31,7 @@ class ResizableImage extends StatefulWidget {
required this.src,
this.height,
this.onDoubleTap,
this.onStateChange,
});

final String src;
Expand All @@ -34,6 +41,7 @@ class ResizableImage extends StatefulWidget {
final Alignment alignment;
final bool editable;
final VoidCallback? onDoubleTap;
final ValueChanged<ResizableImageState>? onStateChange;

final void Function(double width) onResize;

Expand Down Expand Up @@ -96,11 +104,29 @@ class _ResizableImageState extends State<ResizableImage> {
url: widget.src,
width: imageWidth - moveDistance,
userProfilePB: _userProfilePB,
progressIndicatorBuilder: (context, _, __) => _buildLoading(context),
errorWidgetBuilder: (_, __, error) => _ImageLoadFailedWidget(
width: imageWidth,
error: error,
),
onImageLoaded: (isImageInCache) {
if (isImageInCache) {
widget.onStateChange?.call(ResizableImageState.loaded);
}
},
progressIndicatorBuilder: (context, _, progress) {
if (progress.totalSize != null) {
if (progress.progress == 1) {
widget.onStateChange?.call(ResizableImageState.loaded);
} else {
widget.onStateChange?.call(ResizableImageState.loading);
}
}

return _buildLoading(context);
},
errorWidgetBuilder: (_, __, error) {
widget.onStateChange?.call(ResizableImageState.failed);
return _ImageLoadFailedWidget(
width: imageWidth,
error: error,
);
},
);

child = _cacheImage!;
Expand Down
Loading
Loading