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/banner.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_notification.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_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/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_plugins/plugins.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
||||||
import 'package:appflowy/startup/startup.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/log.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
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:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
const _excludeFromDropTarget = [
|
||||||
|
ImageBlockKeys.type,
|
||||||
|
CustomImageBlockKeys.type,
|
||||||
|
MultiImageBlockKeys.type,
|
||||||
|
];
|
||||||
|
|
||||||
class DocumentPage extends StatefulWidget {
|
class DocumentPage extends StatefulWidget {
|
||||||
const DocumentPage({
|
const DocumentPage({
|
||||||
super.key,
|
super.key,
|
||||||
@ -125,15 +135,62 @@ class _DocumentPageState extends State<DocumentPage>
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
child = AppFlowyEditorPage(
|
child = DropTarget(
|
||||||
editorState: state.editorState!,
|
onDragExited: (_) =>
|
||||||
styleCustomizer: EditorStyleCustomizer(
|
state.editorState!.selectionService.removeDropTarget(),
|
||||||
context: context,
|
onDragUpdated: (details) {
|
||||||
// the 44 is the width of the left action list
|
final data = state.editorState!.selectionService
|
||||||
padding: EditorStyleCustomizer.documentPadding,
|
.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),
|
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/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/document/application/document_bloc.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/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/startup/startup.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';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
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:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra/uuid.dart';
|
import 'package:flowy_infra/uuid.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
@ -21,6 +25,42 @@ extension PasteFromImage on EditorState {
|
|||||||
'gif',
|
'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(
|
Future<bool> pasteImage(
|
||||||
String format,
|
String format,
|
||||||
Uint8List imageBytes,
|
Uint8List imageBytes,
|
||||||
|
@ -53,8 +53,8 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: "61d4363"
|
ref: "268aae9"
|
||||||
resolved-ref: "61d4363b4675f7ef91ef5003f2f88bbcb4d9dfa9"
|
resolved-ref: "268aae905b18efc8a3a9c88dc75ebd19b314bd43"
|
||||||
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
|
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
|
||||||
source: git
|
source: git
|
||||||
version: "3.1.0"
|
version: "3.1.0"
|
||||||
@ -387,7 +387,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.2"
|
version: "1.7.2"
|
||||||
cross_file:
|
cross_file:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: cross_file
|
name: cross_file
|
||||||
sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32"
|
sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32"
|
||||||
@ -1118,18 +1118,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: irondash_engine_context
|
name: irondash_engine_context
|
||||||
sha256: "4f5e2629296430cce08cdff42e47cef07b8f74a64fdbdfb0525d147bc1a969a2"
|
sha256: cd7b769db11a2b5243b037c8a9b1ecaef02e1ae27a2d909ffa78c1dad747bb10
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.2"
|
version: "0.5.4"
|
||||||
irondash_message_channel:
|
irondash_message_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: irondash_message_channel
|
name: irondash_message_channel
|
||||||
sha256: dd581214215dca054bd9873209d690ec3609288c28774cb509dbd86b21180cf8
|
sha256: b4101669776509c76133b8917ab8cfc704d3ad92a8c450b92934dd8884a2f060
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.0"
|
version: "0.7.0"
|
||||||
isolates:
|
isolates:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -2053,18 +2053,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: super_clipboard
|
name: super_clipboard
|
||||||
sha256: "15d25eb88df8e904e0c2ef77378c6010cc57bbfc0b6f91f2416d08fad5fcca92"
|
sha256: c72d2ae8c3a66b20a104523add86b7c2813b1d4cced893a9764b84fb97ac8e2a
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.5"
|
version: "0.8.18"
|
||||||
super_native_extensions:
|
super_native_extensions:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: super_native_extensions
|
name: super_native_extensions
|
||||||
sha256: "530a2118d032483b192713c68ed7105fe64418f22492165f87ed01f9b01d4965"
|
sha256: b03f19e54744b65940a7c2cb4f93abd4819b5355aa3464d7b3c9a013b6b76db1
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.12"
|
version: "0.8.18"
|
||||||
sync_http:
|
sync_http:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -142,10 +142,13 @@ dependencies:
|
|||||||
shimmer: ^3.0.0
|
shimmer: ^3.0.0
|
||||||
isolates: ^3.0.3+8
|
isolates: ^3.0.3+8
|
||||||
markdown_widget: ^2.3.2+6
|
markdown_widget: ^2.3.2+6
|
||||||
desktop_drop: ^0.4.4
|
|
||||||
markdown:
|
markdown:
|
||||||
logger: ^2.4.0
|
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 for MacOS and Linux
|
||||||
window_manager: ^0.3.9
|
window_manager: ^0.3.9
|
||||||
|
|
||||||
@ -191,7 +194,7 @@ dependency_overrides:
|
|||||||
appflowy_editor:
|
appflowy_editor:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/AppFlowy-IO/appflowy-editor.git
|
url: https://github.com/AppFlowy-IO/appflowy-editor.git
|
||||||
ref: "61d4363"
|
ref: "268aae9"
|
||||||
|
|
||||||
appflowy_editor_plugins:
|
appflowy_editor_plugins:
|
||||||
git:
|
git:
|
||||||
|
Loading…
Reference in New Issue
Block a user