mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: optimize image upload process and display an error message if upload fails (#4679)
* chore: optimize image upload * feat: show upload image status * chore: upload the ai image to cloud server
This commit is contained in:
parent
252699d249
commit
26f8bbf7c6
@ -104,12 +104,14 @@ class DocumentService {
|
|||||||
/// Upload a file to the cloud storage.
|
/// Upload a file to the cloud storage.
|
||||||
Future<Either<FlowyError, UploadedFilePB>> uploadFile({
|
Future<Either<FlowyError, UploadedFilePB>> uploadFile({
|
||||||
required String localFilePath,
|
required String localFilePath,
|
||||||
|
bool isAsync = true,
|
||||||
}) async {
|
}) async {
|
||||||
final workspace = await FolderEventReadCurrentWorkspace().send();
|
final workspace = await FolderEventReadCurrentWorkspace().send();
|
||||||
return workspace.fold((l) async {
|
return workspace.fold((l) async {
|
||||||
final payload = UploadFileParamsPB(
|
final payload = UploadFileParamsPB(
|
||||||
workspaceId: l.id,
|
workspaceId: l.id,
|
||||||
localFilePath: localFilePath,
|
localFilePath: localFilePath,
|
||||||
|
isAsync: isAsync,
|
||||||
);
|
);
|
||||||
final result = await DocumentEventUploadFile(payload).send();
|
final result = await DocumentEventUploadFile(payload).send();
|
||||||
return result.swap();
|
return result.swap();
|
||||||
|
@ -615,7 +615,7 @@ class DocumentCoverState extends State<DocumentCover> {
|
|||||||
details = await saveImageToLocalStorage(details);
|
details = await saveImageToLocalStorage(details);
|
||||||
} else {
|
} else {
|
||||||
// else we should save the image to cloud storage
|
// else we should save the image to cloud storage
|
||||||
details = await saveImageToCloudStorage(details);
|
(details, _) = await saveImageToCloudStorage(details);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
widget.onChangeCover(type, details);
|
widget.onChangeCover(type, details);
|
||||||
|
@ -44,6 +44,8 @@ class ImagePlaceholderState extends State<ImagePlaceholder> {
|
|||||||
final documentService = DocumentService();
|
final documentService = DocumentService();
|
||||||
late final editorState = context.read<EditorState>();
|
late final editorState = context.read<EditorState>();
|
||||||
|
|
||||||
|
bool showLoading = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final Widget child = DecoratedBox(
|
final Widget child = DecoratedBox(
|
||||||
@ -65,9 +67,19 @@ class ImagePlaceholderState extends State<ImagePlaceholder> {
|
|||||||
size: Size.square(24),
|
size: Size.square(24),
|
||||||
),
|
),
|
||||||
const HSpace(10),
|
const HSpace(10),
|
||||||
FlowyText(
|
...showLoading
|
||||||
LocaleKeys.document_plugins_image_addAnImage.tr(),
|
? [
|
||||||
),
|
FlowyText(
|
||||||
|
LocaleKeys.document_imageBlock_imageIsUploading.tr(),
|
||||||
|
),
|
||||||
|
const HSpace(8),
|
||||||
|
const CircularProgressIndicator.adaptive(),
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
FlowyText(
|
||||||
|
LocaleKeys.document_plugins_image_addAnImage.tr(),
|
||||||
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -188,6 +200,7 @@ class ImagePlaceholderState extends State<ImagePlaceholder> {
|
|||||||
final transaction = editorState.transaction;
|
final transaction = editorState.transaction;
|
||||||
|
|
||||||
String? path;
|
String? path;
|
||||||
|
String? errorMessage;
|
||||||
CustomImageType imageType = CustomImageType.local;
|
CustomImageType imageType = CustomImageType.local;
|
||||||
|
|
||||||
// if the user is using local authenticator, we need to save the image to local storage
|
// if the user is using local authenticator, we need to save the image to local storage
|
||||||
@ -195,14 +208,22 @@ class ImagePlaceholderState extends State<ImagePlaceholder> {
|
|||||||
path = await saveImageToLocalStorage(url);
|
path = await saveImageToLocalStorage(url);
|
||||||
} else {
|
} else {
|
||||||
// else we should save the image to cloud storage
|
// else we should save the image to cloud storage
|
||||||
path = await saveImageToCloudStorage(url);
|
setState(() {
|
||||||
|
showLoading = true;
|
||||||
|
});
|
||||||
|
(path, errorMessage) = await saveImageToCloudStorage(url);
|
||||||
|
setState(() {
|
||||||
|
showLoading = false;
|
||||||
|
});
|
||||||
imageType = CustomImageType.internal;
|
imageType = CustomImageType.internal;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mounted && path == null) {
|
if (mounted && path == null) {
|
||||||
showSnackBarMessage(
|
showSnackBarMessage(
|
||||||
context,
|
context,
|
||||||
LocaleKeys.document_imageBlock_error_invalidImage.tr(),
|
errorMessage == null
|
||||||
|
? LocaleKeys.document_imageBlock_error_invalidImage.tr()
|
||||||
|
: ': $errorMessage',
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -244,12 +265,8 @@ class ImagePlaceholderState extends State<ImagePlaceholder> {
|
|||||||
|
|
||||||
final response = await get(uri);
|
final response = await get(uri);
|
||||||
await File(copyToPath).writeAsBytes(response.bodyBytes);
|
await File(copyToPath).writeAsBytes(response.bodyBytes);
|
||||||
|
await insertLocalImage(copyToPath);
|
||||||
final transaction = editorState.transaction;
|
await File(copyToPath).delete();
|
||||||
transaction.updateNode(widget.node, {
|
|
||||||
ImageBlockKeys.url: copyToPath,
|
|
||||||
});
|
|
||||||
await editorState.apply(transaction);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.error('cannot save image file', e);
|
Log.error('cannot save image file', e);
|
||||||
}
|
}
|
||||||
|
@ -34,19 +34,22 @@ Future<String?> saveImageToLocalStorage(String localImagePath) async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> saveImageToCloudStorage(String localImagePath) async {
|
Future<(String? path, String? errorMessage)> saveImageToCloudStorage(
|
||||||
|
String localImagePath,
|
||||||
|
) async {
|
||||||
final documentService = DocumentService();
|
final documentService = DocumentService();
|
||||||
final result = await documentService.uploadFile(
|
final result = await documentService.uploadFile(
|
||||||
localFilePath: localImagePath,
|
localFilePath: localImagePath,
|
||||||
|
isAsync: false,
|
||||||
);
|
);
|
||||||
return result.fold(
|
return result.fold(
|
||||||
(l) => null,
|
(l) => (null, l.msg),
|
||||||
(r) async {
|
(r) async {
|
||||||
await CustomImageCacheManager().putFile(
|
await CustomImageCacheManager().putFile(
|
||||||
r.url,
|
r.url,
|
||||||
File(localImagePath).readAsBytesSync(),
|
File(localImagePath).readAsBytesSync(),
|
||||||
);
|
);
|
||||||
return r.url;
|
return (r.url, null);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,7 @@ class _ResizableImageState extends State<ResizableImage> {
|
|||||||
progressIndicatorBuilder: (context, url, progress) =>
|
progressIndicatorBuilder: (context, url, progress) =>
|
||||||
_buildLoading(context),
|
_buildLoading(context),
|
||||||
);
|
);
|
||||||
|
|
||||||
child = _cacheImage!;
|
child = _cacheImage!;
|
||||||
} else {
|
} else {
|
||||||
// load local file
|
// load local file
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
Future<bool> isImageExistOnCloud({
|
||||||
|
required String url,
|
||||||
|
required UserProfilePB userProfilePB,
|
||||||
|
}) async {
|
||||||
|
final header = <String, String>{};
|
||||||
|
final token = userProfilePB.token;
|
||||||
|
try {
|
||||||
|
final decodedToken = jsonDecode(token);
|
||||||
|
header['Authorization'] = 'Bearer ${decodedToken['access_token']}';
|
||||||
|
final response = await http.get(Uri.http(url), headers: header);
|
||||||
|
return response.statusCode == 200;
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -862,7 +862,8 @@
|
|||||||
"successToAddImageToGallery": "Image added to gallery successfully",
|
"successToAddImageToGallery": "Image added to gallery successfully",
|
||||||
"unableToLoadImage": "Unable to load image",
|
"unableToLoadImage": "Unable to load image",
|
||||||
"maximumImageSize": "Maximum supported upload image size is 10MB",
|
"maximumImageSize": "Maximum supported upload image size is 10MB",
|
||||||
"uploadImageErrorImageSizeTooBig": "Image size must be less than 10MB"
|
"uploadImageErrorImageSizeTooBig": "Image size must be less than 10MB",
|
||||||
|
"imageIsUploading": "Image is uploading"
|
||||||
},
|
},
|
||||||
"codeBlock": {
|
"codeBlock": {
|
||||||
"language": {
|
"language": {
|
||||||
|
Loading…
Reference in New Issue
Block a user