mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: optimize image block (#4553)
* feat: add tooltip for maximum image size * feat: add maximum upload image size tooltip * feat: limit image size to 10MB * fix: disable copy link option for cloud image * fix: disable copy link option for cloud image * feat: use regex to match the appflowy.cloud image
This commit is contained in:
parent
e9d7d0b7b3
commit
792573f46d
@ -48,9 +48,6 @@ PODS:
|
|||||||
- fluttertoast (0.0.2):
|
- fluttertoast (0.0.2):
|
||||||
- Flutter
|
- Flutter
|
||||||
- Toast
|
- Toast
|
||||||
- FMDB (2.7.5):
|
|
||||||
- FMDB/standard (= 2.7.5)
|
|
||||||
- FMDB/standard (2.7.5)
|
|
||||||
- image_gallery_saver (2.0.2):
|
- image_gallery_saver (2.0.2):
|
||||||
- Flutter
|
- Flutter
|
||||||
- image_picker_ios (0.0.1):
|
- image_picker_ios (0.0.1):
|
||||||
@ -75,19 +72,15 @@ PODS:
|
|||||||
- shared_preferences_foundation (0.0.1):
|
- shared_preferences_foundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- sign_in_with_apple (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- sqflite (0.0.3):
|
- sqflite (0.0.3):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FMDB (>= 2.7.5)
|
- FlutterMacOS
|
||||||
- super_native_extensions (0.0.1):
|
- super_native_extensions (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- SwiftyGif (5.4.3)
|
- SwiftyGif (5.4.3)
|
||||||
- Toast (4.0.0)
|
- Toast (4.0.0)
|
||||||
- url_launcher_ios (0.0.1):
|
- url_launcher_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- webview_flutter_wkwebview (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- app_links (from `.symlinks/plugins/app_links/ios`)
|
- app_links (from `.symlinks/plugins/app_links/ios`)
|
||||||
@ -107,17 +100,14 @@ DEPENDENCIES:
|
|||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
- rich_clipboard_ios (from `.symlinks/plugins/rich_clipboard_ios/ios`)
|
- rich_clipboard_ios (from `.symlinks/plugins/rich_clipboard_ios/ios`)
|
||||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`)
|
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
|
||||||
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
|
||||||
- super_native_extensions (from `.symlinks/plugins/super_native_extensions/ios`)
|
- super_native_extensions (from `.symlinks/plugins/super_native_extensions/ios`)
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`)
|
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
trunk:
|
trunk:
|
||||||
- DKImagePickerController
|
- DKImagePickerController
|
||||||
- DKPhotoGallery
|
- DKPhotoGallery
|
||||||
- FMDB
|
|
||||||
- ReachabilitySwift
|
- ReachabilitySwift
|
||||||
- SDWebImage
|
- SDWebImage
|
||||||
- SwiftyGif
|
- SwiftyGif
|
||||||
@ -158,16 +148,12 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/rich_clipboard_ios/ios"
|
:path: ".symlinks/plugins/rich_clipboard_ios/ios"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||||
sign_in_with_apple:
|
|
||||||
:path: ".symlinks/plugins/sign_in_with_apple/ios"
|
|
||||||
sqflite:
|
sqflite:
|
||||||
:path: ".symlinks/plugins/sqflite/ios"
|
:path: ".symlinks/plugins/sqflite/darwin"
|
||||||
super_native_extensions:
|
super_native_extensions:
|
||||||
:path: ".symlinks/plugins/super_native_extensions/ios"
|
:path: ".symlinks/plugins/super_native_extensions/ios"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||||
webview_flutter_wkwebview:
|
|
||||||
:path: ".symlinks/plugins/webview_flutter_wkwebview/ios"
|
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
app_links: 5ef33d0d295a89d9d16bb81b0e3b0d5f70d6c875
|
app_links: 5ef33d0d295a89d9d16bb81b0e3b0d5f70d6c875
|
||||||
@ -180,25 +166,22 @@ SPEC CHECKSUMS:
|
|||||||
flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc
|
flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc
|
||||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||||
fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c
|
fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c
|
||||||
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
|
||||||
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
|
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
|
||||||
image_picker_ios: 4a8aadfbb6dc30ad5141a2ce3832af9214a705b5
|
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
|
||||||
integration_test: 13825b8a9334a850581300559b8839134b124670
|
integration_test: 13825b8a9334a850581300559b8839134b124670
|
||||||
irondash_engine_context: 3458bf979b90d616ffb8ae03a150bafe2e860cc9
|
irondash_engine_context: 3458bf979b90d616ffb8ae03a150bafe2e860cc9
|
||||||
keyboard_height_plugin: 43fa8bba20fd5c4fdeed5076466b8b9d43cc6b86
|
keyboard_height_plugin: 43fa8bba20fd5c4fdeed5076466b8b9d43cc6b86
|
||||||
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
|
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
|
||||||
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
|
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
||||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||||
rich_clipboard_ios: 7588abe18f881a6d0e9ec0b12e51cae2761e8942
|
rich_clipboard_ios: 7588abe18f881a6d0e9ec0b12e51cae2761e8942
|
||||||
SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84
|
SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84
|
||||||
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
|
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
|
||||||
sign_in_with_apple: f3bf75217ea4c2c8b91823f225d70230119b8440
|
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
||||||
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
|
|
||||||
super_native_extensions: 4916b3c627a9c7fffdc48a23a9eca0b1ac228fa7
|
super_native_extensions: 4916b3c627a9c7fffdc48a23a9eca0b1ac228fa7
|
||||||
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
|
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
|
||||||
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
||||||
url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b
|
url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812
|
||||||
webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a
|
|
||||||
|
|
||||||
PODFILE CHECKSUM: 8c681999c7764593c94846b2a64b44d86f7a27ac
|
PODFILE CHECKSUM: 8c681999c7764593c94846b2a64b44d86f7a27ac
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:appflowy/env/cloud_env.dart';
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||||
@ -14,6 +13,7 @@ import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
|||||||
import 'package:appflowy/shared/appflowy_network_image.dart';
|
import 'package:appflowy/shared/appflowy_network_image.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart' hide UploadImageMenu;
|
import 'package:appflowy_editor/appflowy_editor.dart' hide UploadImageMenu;
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
@ -449,6 +449,7 @@ class DocumentCoverState extends State<DocumentCover> {
|
|||||||
minHeight: 80,
|
minHeight: 80,
|
||||||
),
|
),
|
||||||
child: UploadImageMenu(
|
child: UploadImageMenu(
|
||||||
|
limitMaximumImageSize: !_isLocalMode(),
|
||||||
supportTypes: const [
|
supportTypes: const [
|
||||||
UploadImageType.color,
|
UploadImageType.color,
|
||||||
UploadImageType.local,
|
UploadImageType.local,
|
||||||
@ -574,6 +575,7 @@ class DocumentCoverState extends State<DocumentCover> {
|
|||||||
isPopoverOpen = true;
|
isPopoverOpen = true;
|
||||||
|
|
||||||
return UploadImageMenu(
|
return UploadImageMenu(
|
||||||
|
limitMaximumImageSize: !_isLocalMode(),
|
||||||
supportTypes: const [
|
supportTypes: const [
|
||||||
UploadImageType.color,
|
UploadImageType.color,
|
||||||
UploadImageType.local,
|
UploadImageType.local,
|
||||||
@ -609,9 +611,7 @@ class DocumentCoverState extends State<DocumentCover> {
|
|||||||
|
|
||||||
Future<void> onCoverChanged(CoverType type, String? details) async {
|
Future<void> onCoverChanged(CoverType type, String? details) async {
|
||||||
if (type == CoverType.file && details != null && !isURL(details)) {
|
if (type == CoverType.file && details != null && !isURL(details)) {
|
||||||
final type = await getAuthenticatorType();
|
if (_isLocalMode()) {
|
||||||
// if the user is using local authenticator, we need to save the image to local storage
|
|
||||||
if (type == AuthenticatorType.local) {
|
|
||||||
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
|
||||||
@ -627,6 +627,12 @@ class DocumentCoverState extends State<DocumentCover> {
|
|||||||
isOverlayButtonsHidden = value;
|
isOverlayButtonsHidden = value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isLocalMode() {
|
||||||
|
final userProfilePB = context.read<DocumentBloc>().state.userProfilePB;
|
||||||
|
final type = userProfilePB?.authenticator ?? AuthenticatorPB.Local;
|
||||||
|
return type == AuthenticatorPB.Local;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
|
@ -9,6 +9,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/image/imag
|
|||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/resizeable_image.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/resizeable_image.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/unsupport_image_widget.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/unsupport_image_widget.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
|
import 'package:appflowy/util/string_extension.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart' hide ResizableImage;
|
import 'package:appflowy_editor/appflowy_editor.dart' hide ResizableImage;
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
@ -355,12 +356,15 @@ class CustomImageBlockComponentState extends State<CustomImageBlockComponent>
|
|||||||
|
|
||||||
// only used on mobile platform
|
// only used on mobile platform
|
||||||
List<Widget> _buildExtendActionWidgets(BuildContext context) {
|
List<Widget> _buildExtendActionWidgets(BuildContext context) {
|
||||||
final url = widget.node.attributes[CustomImageBlockKeys.url];
|
final String url = widget.node.attributes[CustomImageBlockKeys.url];
|
||||||
if (!_checkIfURLIsValid(url)) {
|
if (!_checkIfURLIsValid(url)) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
// disable the copy link button if the image is hosted on appflowy cloud
|
||||||
|
// because the url needs the verification token to be accessible
|
||||||
|
if (!url.isAppFlowyCloudUrl)
|
||||||
FlowyOptionTile.text(
|
FlowyOptionTile.text(
|
||||||
showTopBorder: false,
|
showTopBorder: false,
|
||||||
text: LocaleKeys.editor_copyLink.tr(),
|
text: LocaleKeys.editor_copyLink.tr(),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/custom_image_block_component.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/custom_image_block_component.dart';
|
||||||
|
import 'package:appflowy/util/string_extension.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
@ -26,6 +27,8 @@ class ImageMenu extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ImageMenuState extends State<ImageMenu> {
|
class _ImageMenuState extends State<ImageMenu> {
|
||||||
|
late final String? url = widget.node.attributes[ImageBlockKeys.url];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
@ -45,6 +48,9 @@ class _ImageMenuState extends State<ImageMenu> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const HSpace(4),
|
const HSpace(4),
|
||||||
|
// disable the copy link button if the image is hosted on appflowy cloud
|
||||||
|
// because the url needs the verification token to be accessible
|
||||||
|
if (!(url?.isAppFlowyCloudUrl ?? false))
|
||||||
_ImageCopyLinkButton(
|
_ImageCopyLinkButton(
|
||||||
onTap: copyImageLink,
|
onTap: copyImageLink,
|
||||||
),
|
),
|
||||||
@ -64,9 +70,8 @@ class _ImageMenuState extends State<ImageMenu> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void copyImageLink() {
|
void copyImageLink() {
|
||||||
final url = widget.node.attributes[ImageBlockKeys.url];
|
|
||||||
if (url != null) {
|
if (url != null) {
|
||||||
Clipboard.setData(ClipboardData(text: url));
|
Clipboard.setData(ClipboardData(text: url!));
|
||||||
showSnackBarMessage(
|
showSnackBarMessage(
|
||||||
context,
|
context,
|
||||||
LocaleKeys.document_plugins_image_copiedToPasteBoard.tr(),
|
LocaleKeys.document_plugins_image_copiedToPasteBoard.tr(),
|
||||||
|
@ -9,6 +9,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/image/cust
|
|||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/image_util.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/image_util.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
|
import 'package:appflowy/util/file_extension.dart';
|
||||||
import 'package:appflowy/workspace/application/settings/application_data_storage.dart';
|
import 'package:appflowy/workspace/application/settings/application_data_storage.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
@ -85,6 +86,7 @@ class ImagePlaceholderState extends State<ImagePlaceholder> {
|
|||||||
clickHandler: PopoverClickHandler.gestureDetector,
|
clickHandler: PopoverClickHandler.gestureDetector,
|
||||||
popupBuilder: (context) {
|
popupBuilder: (context) {
|
||||||
return UploadImageMenu(
|
return UploadImageMenu(
|
||||||
|
limitMaximumImageSize: !_isLocalMode(),
|
||||||
onSelectedLocalImage: (path) {
|
onSelectedLocalImage: (path) {
|
||||||
controller.close();
|
controller.close();
|
||||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||||
@ -126,6 +128,7 @@ class ImagePlaceholderState extends State<ImagePlaceholder> {
|
|||||||
if (PlatformExtension.isDesktopOrWeb) {
|
if (PlatformExtension.isDesktopOrWeb) {
|
||||||
controller.show();
|
controller.show();
|
||||||
} else {
|
} else {
|
||||||
|
final isLocalMode = _isLocalMode();
|
||||||
showMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
title: LocaleKeys.editor_image.tr(),
|
title: LocaleKeys.editor_image.tr(),
|
||||||
@ -140,6 +143,7 @@ class ImagePlaceholderState extends State<ImagePlaceholder> {
|
|||||||
minHeight: 80,
|
minHeight: 80,
|
||||||
),
|
),
|
||||||
child: UploadImageMenu(
|
child: UploadImageMenu(
|
||||||
|
limitMaximumImageSize: !isLocalMode,
|
||||||
supportTypes: const [
|
supportTypes: const [
|
||||||
UploadImageType.local,
|
UploadImageType.local,
|
||||||
UploadImageType.url,
|
UploadImageType.url,
|
||||||
@ -170,15 +174,24 @@ class ImagePlaceholderState extends State<ImagePlaceholder> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final userProfilePB = context.read<DocumentBloc>().state.userProfilePB;
|
final size = url.fileSize;
|
||||||
|
if (size == null || size > 10 * 1024 * 1024) {
|
||||||
|
// show error
|
||||||
|
controller.close();
|
||||||
|
showSnackBarMessage(
|
||||||
|
context,
|
||||||
|
LocaleKeys.document_imageBlock_uploadImageErrorImageSizeTooBig.tr(),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final transaction = editorState.transaction;
|
final transaction = editorState.transaction;
|
||||||
final type = userProfilePB?.authenticator ?? AuthenticatorPB.Local;
|
|
||||||
String? path;
|
String? path;
|
||||||
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
|
||||||
if (type == AuthenticatorPB.Local) {
|
if (_isLocalMode()) {
|
||||||
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
|
||||||
@ -258,4 +271,10 @@ class ImagePlaceholderState extends State<ImagePlaceholder> {
|
|||||||
});
|
});
|
||||||
await editorState.apply(transaction);
|
await editorState.apply(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isLocalMode() {
|
||||||
|
final userProfilePB = context.read<DocumentBloc>().state.userProfilePB;
|
||||||
|
final type = userProfilePB?.authenticator ?? AuthenticatorPB.Local;
|
||||||
|
return type == AuthenticatorPB.Local;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ class UploadImageMenu extends StatefulWidget {
|
|||||||
required this.onSelectedNetworkImage,
|
required this.onSelectedNetworkImage,
|
||||||
this.onSelectedColor,
|
this.onSelectedColor,
|
||||||
this.supportTypes = UploadImageType.values,
|
this.supportTypes = UploadImageType.values,
|
||||||
|
this.limitMaximumImageSize = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
final void Function(String? path) onSelectedLocalImage;
|
final void Function(String? path) onSelectedLocalImage;
|
||||||
@ -55,6 +56,7 @@ class UploadImageMenu extends StatefulWidget {
|
|||||||
final void Function(String url) onSelectedNetworkImage;
|
final void Function(String url) onSelectedNetworkImage;
|
||||||
final void Function(String color)? onSelectedColor;
|
final void Function(String color)? onSelectedColor;
|
||||||
final List<UploadImageType> supportTypes;
|
final List<UploadImageType> supportTypes;
|
||||||
|
final bool limitMaximumImageSize;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<UploadImageMenu> createState() => _UploadImageMenuState();
|
State<UploadImageMenu> createState() => _UploadImageMenuState();
|
||||||
@ -151,9 +153,21 @@ class _UploadImageMenuState extends State<UploadImageMenu> {
|
|||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
constraints: constraints,
|
constraints: constraints,
|
||||||
child: UploadImageFileWidget(
|
child: Column(
|
||||||
|
children: [
|
||||||
|
UploadImageFileWidget(
|
||||||
onPickFile: widget.onSelectedLocalImage,
|
onPickFile: widget.onSelectedLocalImage,
|
||||||
),
|
),
|
||||||
|
if (widget.limitMaximumImageSize) ...[
|
||||||
|
const VSpace(6.0),
|
||||||
|
FlowyText(
|
||||||
|
LocaleKeys.document_imageBlock_maximumImageSize.tr(),
|
||||||
|
fontSize: 12.0,
|
||||||
|
color: Theme.of(context).hintColor,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
case UploadImageType.url:
|
case UploadImageType.url:
|
||||||
return Container(
|
return Container(
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:appflowy/shared/custom_image_cache_manager.dart';
|
import 'package:appflowy/shared/custom_image_cache_manager.dart';
|
||||||
|
import 'package:appflowy/util/string_extension.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -33,7 +34,7 @@ class FlowyNetworkImage extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
assert(isURL(url));
|
assert(isURL(url));
|
||||||
|
|
||||||
if (url.contains('beta.appflowy')) {
|
if (url.isAppFlowyCloudUrl) {
|
||||||
assert(userProfilePB != null && userProfilePB!.token.isNotEmpty);
|
assert(userProfilePB != null && userProfilePB!.token.isNotEmpty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
11
frontend/appflowy_flutter/lib/util/file_extension.dart
Normal file
11
frontend/appflowy_flutter/lib/util/file_extension.dart
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
extension FileSizeExtension on String {
|
||||||
|
int? get fileSize {
|
||||||
|
final file = File(this);
|
||||||
|
if (file.existsSync()) {
|
||||||
|
return file.lengthSync();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
extension EncodeString on String {
|
extension StringExtension on String {
|
||||||
static const _specialCharacters = r'\/:*?"<>| ';
|
static const _specialCharacters = r'\/:*?"<>| ';
|
||||||
|
|
||||||
/// Encode a string to a file name.
|
/// Encode a string to a file name.
|
||||||
@ -17,4 +19,20 @@ extension EncodeString on String {
|
|||||||
}
|
}
|
||||||
return buffer.toString();
|
return buffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the file size of the file at the given path.
|
||||||
|
///
|
||||||
|
/// Returns null if the file does not exist.
|
||||||
|
int? get fileSize {
|
||||||
|
final file = File(this);
|
||||||
|
if (file.existsSync()) {
|
||||||
|
return file.lengthSync();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns if the string is a appflowy cloud url.
|
||||||
|
bool get isAppFlowyCloudUrl {
|
||||||
|
return RegExp(r'^(https:\/\/)(.*)(\.appflowy\.cloud\/)(.*)').hasMatch(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -835,7 +835,9 @@
|
|||||||
"saveImageToGallery": "Save image",
|
"saveImageToGallery": "Save image",
|
||||||
"failedToAddImageToGallery": "Failed to add image to gallery",
|
"failedToAddImageToGallery": "Failed to add image to gallery",
|
||||||
"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",
|
||||||
|
"uploadImageErrorImageSizeTooBig": "Image size must be less than 10MB"
|
||||||
},
|
},
|
||||||
"codeBlock": {
|
"codeBlock": {
|
||||||
"language": {
|
"language": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user