mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: convert link preview block to url text (#4734)
This commit is contained in:
parent
b75947b630
commit
93af6e69a1
@ -188,6 +188,7 @@ Map<String, BlockComponentBuilder> getEditorBuilderMap({
|
|||||||
),
|
),
|
||||||
builder: (context, node, url, title, description, imageUrl) =>
|
builder: (context, node, url, title, description, imageUrl) =>
|
||||||
CustomLinkPreviewWidget(
|
CustomLinkPreviewWidget(
|
||||||
|
node: node,
|
||||||
url: url,
|
url: url,
|
||||||
title: title,
|
title: title,
|
||||||
description: description,
|
description: description,
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
|
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class MenuBlockButton extends StatelessWidget {
|
||||||
|
const MenuBlockButton({
|
||||||
|
super.key,
|
||||||
|
required this.tooltip,
|
||||||
|
required this.iconData,
|
||||||
|
this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
final String tooltip;
|
||||||
|
final FlowySvgData iconData;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FlowyButton(
|
||||||
|
useIntrinsicWidth: true,
|
||||||
|
onTap: onTap,
|
||||||
|
text: FlowyTooltip(
|
||||||
|
message: tooltip,
|
||||||
|
child: FlowySvg(
|
||||||
|
iconData,
|
||||||
|
size: const Size.square(16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,15 @@
|
|||||||
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/align_toolbar_item/align_toolbar_item.dart';
|
||||||
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/block_menu/block_menu_button.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/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||||
import 'package:appflowy/util/string_extension.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';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flowy_infra_ui/widget/ignore_parent_gesture.dart';
|
import 'package:flowy_infra_ui/widget/ignore_parent_gesture.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
@ -51,18 +53,23 @@ class _ImageMenuState extends State<ImageMenu> {
|
|||||||
const HSpace(4),
|
const HSpace(4),
|
||||||
// disable the copy link button if the image is hosted on appflowy cloud
|
// disable the copy link button if the image is hosted on appflowy cloud
|
||||||
// because the url needs the verification token to be accessible
|
// because the url needs the verification token to be accessible
|
||||||
if (!(url?.isAppFlowyCloudUrl ?? false))
|
if (!(url?.isAppFlowyCloudUrl ?? false)) ...[
|
||||||
_ImageCopyLinkButton(
|
MenuBlockButton(
|
||||||
|
tooltip: LocaleKeys.editor_copyLink.tr(),
|
||||||
|
iconData: FlowySvgs.copy_s,
|
||||||
onTap: copyImageLink,
|
onTap: copyImageLink,
|
||||||
),
|
),
|
||||||
const HSpace(4),
|
const HSpace(4),
|
||||||
|
],
|
||||||
_ImageAlignButton(
|
_ImageAlignButton(
|
||||||
node: widget.node,
|
node: widget.node,
|
||||||
state: widget.state,
|
state: widget.state,
|
||||||
),
|
),
|
||||||
const _Divider(),
|
const _Divider(),
|
||||||
_ImageDeleteButton(
|
MenuBlockButton(
|
||||||
onTap: () => deleteImage(),
|
tooltip: LocaleKeys.button_delete.tr(),
|
||||||
|
iconData: FlowySvgs.delete_s,
|
||||||
|
onTap: deleteImage,
|
||||||
),
|
),
|
||||||
const HSpace(4),
|
const HSpace(4),
|
||||||
],
|
],
|
||||||
@ -90,29 +97,6 @@ class _ImageMenuState extends State<ImageMenu> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ImageCopyLinkButton extends StatelessWidget {
|
|
||||||
const _ImageCopyLinkButton({
|
|
||||||
required this.onTap,
|
|
||||||
});
|
|
||||||
|
|
||||||
final VoidCallback onTap;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return FlowyButton(
|
|
||||||
useIntrinsicWidth: true,
|
|
||||||
onTap: onTap,
|
|
||||||
text: FlowyTooltip(
|
|
||||||
message: LocaleKeys.editor_copyLink.tr(),
|
|
||||||
child: const FlowySvg(
|
|
||||||
FlowySvgs.copy_s,
|
|
||||||
size: Size.square(16),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ImageAlignButton extends StatefulWidget {
|
class _ImageAlignButton extends StatefulWidget {
|
||||||
const _ImageAlignButton({
|
const _ImageAlignButton({
|
||||||
required this.node,
|
required this.node,
|
||||||
@ -162,7 +146,11 @@ class _ImageAlignButtonState extends State<_ImageAlignButton> {
|
|||||||
margin: const EdgeInsets.all(0),
|
margin: const EdgeInsets.all(0),
|
||||||
direction: PopoverDirection.bottomWithCenterAligned,
|
direction: PopoverDirection.bottomWithCenterAligned,
|
||||||
offset: const Offset(0, 10),
|
offset: const Offset(0, 10),
|
||||||
child: buildAlignIcon(),
|
child: MenuBlockButton(
|
||||||
|
tooltip: LocaleKeys.document_plugins_optionAction_align.tr(),
|
||||||
|
iconData: iconFor(align),
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
popupBuilder: (_) {
|
popupBuilder: (_) {
|
||||||
preventMenuClose();
|
preventMenuClose();
|
||||||
return _AlignButtons(
|
return _AlignButtons(
|
||||||
@ -210,19 +198,6 @@ class _ImageAlignButtonState extends State<_ImageAlignButton> {
|
|||||||
return FlowySvgs.align_left_s;
|
return FlowySvgs.align_left_s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildAlignIcon() {
|
|
||||||
return FlowyButton(
|
|
||||||
useIntrinsicWidth: true,
|
|
||||||
text: FlowyTooltip(
|
|
||||||
message: LocaleKeys.document_plugins_optionAction_align.tr(),
|
|
||||||
child: FlowySvg(
|
|
||||||
iconFor(align),
|
|
||||||
size: const Size.square(16),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AlignButtons extends StatelessWidget {
|
class _AlignButtons extends StatelessWidget {
|
||||||
@ -240,19 +215,22 @@ class _AlignButtons extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const HSpace(4),
|
const HSpace(4),
|
||||||
_AlignButton(
|
MenuBlockButton(
|
||||||
icon: FlowySvgs.align_left_s,
|
tooltip: LocaleKeys.document_plugins_optionAction_left,
|
||||||
onTap: () => onAlignChanged('left'),
|
iconData: FlowySvgs.align_left_s,
|
||||||
|
onTap: () => onAlignChanged(leftAlignmentKey),
|
||||||
),
|
),
|
||||||
const _Divider(),
|
const _Divider(),
|
||||||
_AlignButton(
|
MenuBlockButton(
|
||||||
icon: FlowySvgs.align_center_s,
|
tooltip: LocaleKeys.document_plugins_optionAction_center,
|
||||||
onTap: () => onAlignChanged('center'),
|
iconData: FlowySvgs.align_center_s,
|
||||||
|
onTap: () => onAlignChanged(centerAlignmentKey),
|
||||||
),
|
),
|
||||||
const _Divider(),
|
const _Divider(),
|
||||||
_AlignButton(
|
MenuBlockButton(
|
||||||
icon: FlowySvgs.align_right_s,
|
tooltip: LocaleKeys.document_plugins_optionAction_right,
|
||||||
onTap: () => onAlignChanged('right'),
|
iconData: FlowySvgs.align_right_s,
|
||||||
|
onTap: () => onAlignChanged(rightAlignmentKey),
|
||||||
),
|
),
|
||||||
const HSpace(4),
|
const HSpace(4),
|
||||||
],
|
],
|
||||||
@ -261,51 +239,6 @@ class _AlignButtons extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AlignButton extends StatelessWidget {
|
|
||||||
const _AlignButton({
|
|
||||||
required this.icon,
|
|
||||||
required this.onTap,
|
|
||||||
});
|
|
||||||
|
|
||||||
final FlowySvgData icon;
|
|
||||||
final VoidCallback onTap;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return FlowyButton(
|
|
||||||
useIntrinsicWidth: true,
|
|
||||||
onTap: onTap,
|
|
||||||
text: FlowySvg(
|
|
||||||
icon,
|
|
||||||
size: const Size.square(16),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ImageDeleteButton extends StatelessWidget {
|
|
||||||
const _ImageDeleteButton({
|
|
||||||
required this.onTap,
|
|
||||||
});
|
|
||||||
|
|
||||||
final VoidCallback onTap;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return FlowyButton(
|
|
||||||
useIntrinsicWidth: true,
|
|
||||||
onTap: onTap,
|
|
||||||
text: FlowyTooltip(
|
|
||||||
message: LocaleKeys.button_delete.tr(),
|
|
||||||
child: const FlowySvg(
|
|
||||||
FlowySvgs.delete_s,
|
|
||||||
size: Size.square(16),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _Divider extends StatelessWidget {
|
class _Divider extends StatelessWidget {
|
||||||
const _Divider();
|
const _Divider();
|
||||||
|
|
||||||
|
@ -1,17 +1,28 @@
|
|||||||
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||||
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/mobile_block_action_buttons.dart';
|
||||||
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/link_preview/shared.dart';
|
||||||
import 'package:appflowy/shared/appflowy_network_image.dart';
|
import 'package:appflowy/shared/appflowy_network_image.dart';
|
||||||
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
class CustomLinkPreviewWidget extends StatelessWidget {
|
class CustomLinkPreviewWidget extends StatelessWidget {
|
||||||
const CustomLinkPreviewWidget({
|
const CustomLinkPreviewWidget({
|
||||||
super.key,
|
super.key,
|
||||||
|
required this.node,
|
||||||
required this.url,
|
required this.url,
|
||||||
this.title,
|
this.title,
|
||||||
this.description,
|
this.description,
|
||||||
this.imageUrl,
|
this.imageUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final Node node;
|
||||||
final String? title;
|
final String? title;
|
||||||
final String? description;
|
final String? description;
|
||||||
final String? imageUrl;
|
final String? imageUrl;
|
||||||
@ -19,9 +30,17 @@ class CustomLinkPreviewWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return InkWell(
|
final documentFontSize = context
|
||||||
onTap: () => launchUrlString(url),
|
.read<EditorState>()
|
||||||
child: Container(
|
.editorStyle
|
||||||
|
.textStyleConfiguration
|
||||||
|
.text
|
||||||
|
.fontSize ??
|
||||||
|
16.0;
|
||||||
|
final (fontSize, width) = PlatformExtension.isDesktopOrWeb
|
||||||
|
? (documentFontSize, 180.0)
|
||||||
|
: (documentFontSize - 2, 120.0);
|
||||||
|
final Widget child = Container(
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
@ -31,8 +50,9 @@ class CustomLinkPreviewWidget extends StatelessWidget {
|
|||||||
6.0,
|
6.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
child: IntrinsicHeight(
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
if (imageUrl != null)
|
if (imageUrl != null)
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
@ -42,8 +62,7 @@ class CustomLinkPreviewWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: FlowyNetworkImage(
|
child: FlowyNetworkImage(
|
||||||
url: imageUrl!,
|
url: imageUrl!,
|
||||||
width: 180,
|
width: width,
|
||||||
height: 120,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -55,12 +74,15 @@ class CustomLinkPreviewWidget extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
if (title != null)
|
if (title != null)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 4.0),
|
padding: const EdgeInsets.only(
|
||||||
|
bottom: 4.0,
|
||||||
|
right: 10.0,
|
||||||
|
),
|
||||||
child: FlowyText.medium(
|
child: FlowyText.medium(
|
||||||
title!,
|
title!,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
fontSize: 16.0,
|
fontSize: fontSize,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (description != null)
|
if (description != null)
|
||||||
@ -70,6 +92,7 @@ class CustomLinkPreviewWidget extends StatelessWidget {
|
|||||||
description!,
|
description!,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
|
fontSize: fontSize - 4,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
FlowyText(
|
FlowyText(
|
||||||
@ -77,6 +100,7 @@ class CustomLinkPreviewWidget extends StatelessWidget {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
color: Theme.of(context).hintColor,
|
color: Theme.of(context).hintColor,
|
||||||
|
fontSize: fontSize - 4,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -86,5 +110,40 @@ class CustomLinkPreviewWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (PlatformExtension.isDesktopOrWeb) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: () => launchUrlString(url),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return MobileBlockActionButtons(
|
||||||
|
node: node,
|
||||||
|
editorState: context.read<EditorState>(),
|
||||||
|
extendActionWidgets: _buildExtendActionWidgets(context),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// only used on mobile platform
|
||||||
|
List<Widget> _buildExtendActionWidgets(BuildContext context) {
|
||||||
|
return [
|
||||||
|
FlowyOptionTile.text(
|
||||||
|
showTopBorder: false,
|
||||||
|
text: LocaleKeys.document_plugins_urlPreview_convertToLink.tr(),
|
||||||
|
leftIcon: const FlowySvg(
|
||||||
|
FlowySvgs.m_aa_link_s,
|
||||||
|
size: Size.square(20),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
context.pop();
|
||||||
|
convertUrlPreviewNodeToLink(
|
||||||
|
context.read<EditorState>(),
|
||||||
|
node,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +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/block_menu/block_menu_button.dart';
|
||||||
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/link_preview/shared.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_editor_plugins/appflowy_editor_plugins.dart';
|
import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
|
||||||
@ -43,11 +45,24 @@ class _LinkPreviewMenuState extends State<LinkPreviewMenu> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const HSpace(4),
|
const HSpace(4),
|
||||||
_CopyLinkButton(
|
MenuBlockButton(
|
||||||
|
tooltip: LocaleKeys.document_plugins_urlPreview_convertToLink.tr(),
|
||||||
|
iconData: FlowySvgs.m_aa_link_s,
|
||||||
|
onTap: () => convertUrlPreviewNodeToLink(
|
||||||
|
context.read<EditorState>(),
|
||||||
|
widget.node,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const HSpace(4),
|
||||||
|
MenuBlockButton(
|
||||||
|
tooltip: LocaleKeys.editor_copyLink.tr(),
|
||||||
|
iconData: FlowySvgs.copy_s,
|
||||||
onTap: copyImageLink,
|
onTap: copyImageLink,
|
||||||
),
|
),
|
||||||
const _Divider(),
|
const _Divider(),
|
||||||
_DeleteButton(
|
MenuBlockButton(
|
||||||
|
tooltip: LocaleKeys.button_delete.tr(),
|
||||||
|
iconData: FlowySvgs.delete_s,
|
||||||
onTap: deleteLinkPreviewNode,
|
onTap: deleteLinkPreviewNode,
|
||||||
),
|
),
|
||||||
const HSpace(4),
|
const HSpace(4),
|
||||||
@ -77,44 +92,6 @@ class _LinkPreviewMenuState extends State<LinkPreviewMenu> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CopyLinkButton extends StatelessWidget {
|
|
||||||
const _CopyLinkButton({
|
|
||||||
required this.onTap,
|
|
||||||
});
|
|
||||||
|
|
||||||
final VoidCallback onTap;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return InkWell(
|
|
||||||
onTap: onTap,
|
|
||||||
child: const FlowySvg(
|
|
||||||
FlowySvgs.copy_s,
|
|
||||||
size: Size.square(16),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _DeleteButton extends StatelessWidget {
|
|
||||||
const _DeleteButton({
|
|
||||||
required this.onTap,
|
|
||||||
});
|
|
||||||
|
|
||||||
final VoidCallback onTap;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return InkWell(
|
|
||||||
onTap: onTap,
|
|
||||||
child: const FlowySvg(
|
|
||||||
FlowySvgs.delete_s,
|
|
||||||
size: Size.square(16),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _Divider extends StatelessWidget {
|
class _Divider extends StatelessWidget {
|
||||||
const _Divider();
|
const _Divider();
|
||||||
|
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
|
||||||
|
|
||||||
|
void convertUrlPreviewNodeToLink(EditorState editorState, Node node) {
|
||||||
|
assert(node.type == LinkPreviewBlockKeys.type);
|
||||||
|
final url = node.attributes[ImageBlockKeys.url];
|
||||||
|
final transaction = editorState.transaction;
|
||||||
|
transaction
|
||||||
|
..insertNode(node.path, paragraphNode(text: url))
|
||||||
|
..deleteNode(node);
|
||||||
|
transaction.afterSelection = Selection.collapsed(
|
||||||
|
Position(
|
||||||
|
path: node.path,
|
||||||
|
offset: url.length,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
editorState.apply(transaction);
|
||||||
|
}
|
@ -790,7 +790,8 @@
|
|||||||
"imageUploadFailed": "Image upload failed"
|
"imageUploadFailed": "Image upload failed"
|
||||||
},
|
},
|
||||||
"urlPreview": {
|
"urlPreview": {
|
||||||
"copiedToPasteBoard": "The link has been copied to the clipboard"
|
"copiedToPasteBoard": "The link has been copied to the clipboard",
|
||||||
|
"convertToLink": "Convert to embed link"
|
||||||
},
|
},
|
||||||
"outline": {
|
"outline": {
|
||||||
"addHeadingToCreateOutline": "Add headings to create a table of contents.",
|
"addHeadingToCreateOutline": "Add headings to create a table of contents.",
|
||||||
|
Loading…
Reference in New Issue
Block a user