mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: drop images into editor (#5813)
This commit is contained in:
parent
23b6f94e82
commit
d1af172fb7
@ -6,7 +6,10 @@ import 'package:appflowy/plugins/document/application/document_bloc.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/banner.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_notification.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_page.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/paste_from_image.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/cover/document_immersive_cover.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/custom_image_block_component/custom_image_block_component.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/multi_image_block_component/multi_image_block_component.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
@ -16,10 +19,17 @@ import 'package:appflowy/workspace/application/view/prelude.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
||||
import 'package:desktop_drop/desktop_drop.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
const _excludeFromDropTarget = [
|
||||
ImageBlockKeys.type,
|
||||
CustomImageBlockKeys.type,
|
||||
MultiImageBlockKeys.type,
|
||||
];
|
||||
|
||||
class DocumentPage extends StatefulWidget {
|
||||
const DocumentPage({
|
||||
super.key,
|
||||
@ -125,15 +135,62 @@ class _DocumentPageState extends State<DocumentPage>
|
||||
},
|
||||
);
|
||||
} else {
|
||||
child = AppFlowyEditorPage(
|
||||
editorState: state.editorState!,
|
||||
styleCustomizer: EditorStyleCustomizer(
|
||||
context: context,
|
||||
// the 44 is the width of the left action list
|
||||
padding: EditorStyleCustomizer.documentPadding,
|
||||
child = DropTarget(
|
||||
onDragExited: (_) =>
|
||||
state.editorState!.selectionService.removeDropTarget(),
|
||||
onDragUpdated: (details) {
|
||||
final data = state.editorState!.selectionService
|
||||
.getDropTargetRenderData(details.globalPosition);
|
||||
|
||||
if (data != null &&
|
||||
data.dropTarget != null &&
|
||||
|
||||
// We implement custom Drop logic for image blocks, this is
|
||||
// how we can exclude them from the Drop Target
|
||||
!_excludeFromDropTarget.contains(data.cursorNode?.type)) {
|
||||
// Render the drop target
|
||||
state.editorState!.selectionService
|
||||
.renderDropTargetForOffset(details.globalPosition);
|
||||
} else {
|
||||
state.editorState!.selectionService.removeDropTarget();
|
||||
}
|
||||
},
|
||||
onDragDone: (details) async {
|
||||
state.editorState!.selectionService.removeDropTarget();
|
||||
|
||||
final data = state.editorState!.selectionService
|
||||
.getDropTargetRenderData(details.globalPosition);
|
||||
|
||||
if (data != null) {
|
||||
if (data.cursorNode != null) {
|
||||
if ([
|
||||
ImageBlockKeys.type,
|
||||
CustomImageBlockKeys.type,
|
||||
MultiImageBlockKeys.type,
|
||||
].contains(data.cursorNode?.type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final isLocalMode = context.read<DocumentBloc>().isLocalMode;
|
||||
await editorState!.dropImages(
|
||||
data.dropTarget!,
|
||||
details.files,
|
||||
widget.view.id,
|
||||
isLocalMode,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: AppFlowyEditorPage(
|
||||
editorState: state.editorState!,
|
||||
styleCustomizer: EditorStyleCustomizer(
|
||||
context: context,
|
||||
// the 44 is the width of the left action list
|
||||
padding: EditorStyleCustomizer.documentPadding,
|
||||
),
|
||||
header: _buildCoverAndIcon(context, state),
|
||||
initialSelection: widget.initialSelection,
|
||||
),
|
||||
header: _buildCoverAndIcon(context, state),
|
||||
initialSelection: widget.initialSelection,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -353,6 +353,10 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
},
|
||||
child: VSpace(PlatformExtension.isDesktopOrWeb ? 200 : 400),
|
||||
),
|
||||
dropTargetStyle: AppFlowyDropTargetStyle(
|
||||
color: Theme.of(context).colorScheme.primary.withOpacity(0.8),
|
||||
margin: const EdgeInsets.only(left: 44),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -3,12 +3,16 @@ import 'dart:typed_data';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/application/document_bloc.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/common.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/custom_image_block_component/custom_image_block_component.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/image_util.dart';
|
||||
import 'package:appflowy/shared/patterns/common_patterns.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/application/settings/application_data_storage.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
||||
import 'package:cross_file/cross_file.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/uuid.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
@ -21,6 +25,42 @@ extension PasteFromImage on EditorState {
|
||||
'gif',
|
||||
];
|
||||
|
||||
Future<void> dropImages(
|
||||
Node dropNode,
|
||||
List<XFile> files,
|
||||
String documentId,
|
||||
bool isLocalMode,
|
||||
) async {
|
||||
final imageFiles = files.where(
|
||||
(file) =>
|
||||
file.mimeType?.startsWith('image/') ??
|
||||
false || imgExtensionRegex.hasMatch(file.name),
|
||||
);
|
||||
|
||||
for (final file in imageFiles) {
|
||||
String? path;
|
||||
CustomImageType? type;
|
||||
if (isLocalMode) {
|
||||
path = await saveImageToLocalStorage(file.path);
|
||||
type = CustomImageType.local;
|
||||
} else {
|
||||
(path, _) = await saveImageToCloudStorage(file.path, documentId);
|
||||
type = CustomImageType.internal;
|
||||
}
|
||||
|
||||
if (path == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final t = transaction
|
||||
..insertNode(
|
||||
dropNode.path,
|
||||
customImageNode(url: path, type: type),
|
||||
);
|
||||
await apply(t);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> pasteImage(
|
||||
String format,
|
||||
Uint8List imageBytes,
|
||||
|
@ -53,8 +53,8 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: "61d4363"
|
||||
resolved-ref: "61d4363b4675f7ef91ef5003f2f88bbcb4d9dfa9"
|
||||
ref: "268aae9"
|
||||
resolved-ref: "268aae905b18efc8a3a9c88dc75ebd19b314bd43"
|
||||
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
|
||||
source: git
|
||||
version: "3.1.0"
|
||||
@ -387,7 +387,7 @@ packages:
|
||||
source: hosted
|
||||
version: "1.7.2"
|
||||
cross_file:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cross_file
|
||||
sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32"
|
||||
@ -1118,18 +1118,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: irondash_engine_context
|
||||
sha256: "4f5e2629296430cce08cdff42e47cef07b8f74a64fdbdfb0525d147bc1a969a2"
|
||||
sha256: cd7b769db11a2b5243b037c8a9b1ecaef02e1ae27a2d909ffa78c1dad747bb10
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.2"
|
||||
version: "0.5.4"
|
||||
irondash_message_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: irondash_message_channel
|
||||
sha256: dd581214215dca054bd9873209d690ec3609288c28774cb509dbd86b21180cf8
|
||||
sha256: b4101669776509c76133b8917ab8cfc704d3ad92a8c450b92934dd8884a2f060
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.0"
|
||||
version: "0.7.0"
|
||||
isolates:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -2053,18 +2053,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: super_clipboard
|
||||
sha256: "15d25eb88df8e904e0c2ef77378c6010cc57bbfc0b6f91f2416d08fad5fcca92"
|
||||
sha256: c72d2ae8c3a66b20a104523add86b7c2813b1d4cced893a9764b84fb97ac8e2a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.5"
|
||||
version: "0.8.18"
|
||||
super_native_extensions:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: super_native_extensions
|
||||
sha256: "530a2118d032483b192713c68ed7105fe64418f22492165f87ed01f9b01d4965"
|
||||
sha256: b03f19e54744b65940a7c2cb4f93abd4819b5355aa3464d7b3c9a013b6b76db1
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.12"
|
||||
version: "0.8.18"
|
||||
sync_http:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -142,10 +142,13 @@ dependencies:
|
||||
shimmer: ^3.0.0
|
||||
isolates: ^3.0.3+8
|
||||
markdown_widget: ^2.3.2+6
|
||||
desktop_drop: ^0.4.4
|
||||
markdown:
|
||||
logger: ^2.4.0
|
||||
|
||||
# Desktop Drop uses Cross File (XFile) data type
|
||||
desktop_drop: ^0.4.4
|
||||
cross_file: ^0.3.4+1
|
||||
|
||||
# Window Manager for MacOS and Linux
|
||||
window_manager: ^0.3.9
|
||||
|
||||
@ -191,7 +194,7 @@ dependency_overrides:
|
||||
appflowy_editor:
|
||||
git:
|
||||
url: https://github.com/AppFlowy-IO/appflowy-editor.git
|
||||
ref: "61d4363"
|
||||
ref: "268aae9"
|
||||
|
||||
appflowy_editor_plugins:
|
||||
git:
|
||||
|
Loading…
Reference in New Issue
Block a user