diff --git a/frontend/appflowy_web_app/src/application/collab.type.ts b/frontend/appflowy_web_app/src/application/collab.type.ts index d47b87cb05..abef418897 100644 --- a/frontend/appflowy_web_app/src/application/collab.type.ts +++ b/frontend/appflowy_web_app/src/application/collab.type.ts @@ -32,6 +32,7 @@ export enum BlockType { OutlineBlock = 'outline', TableBlock = 'table', TableCell = 'table/cell', + LinkPreview = 'link_preview', } export enum InlineBlockType { @@ -79,6 +80,10 @@ export interface MathEquationBlockData extends BlockData { formula?: string; } +export interface LinkPreviewBlockData extends BlockData { + url?: string; +} + export enum ImageType { Local = 0, Internal = 1, diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/link-preview/LinkPreview.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/link-preview/LinkPreview.tsx new file mode 100644 index 0000000000..10c5df30c9 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/link-preview/LinkPreview.tsx @@ -0,0 +1,68 @@ +import { EditorElementProps, LinkPreviewNode } from '@/components/editor/editor.type'; +import axios from 'axios'; +import React, { forwardRef, memo, useEffect, useState } from 'react'; + +export const LinkPreview = memo( + forwardRef>(({ node, children, ...attributes }, ref) => { + const [data, setData] = useState<{ + image: { url: string }; + title: string; + description: string; + } | null>(null); + const url = node.data.url; + + useEffect(() => { + if (!url) return; + + setData(null); + void (async () => { + try { + const response = await axios.get(`https://api.microlink.io/?url=${url}`); + + if (response.data.statusCode !== 200) return; + const data = response.data.data; + + setData(data); + } catch (error) { + // don't do anything + } + })(); + }, [url]); + return ( +
{ + window.open(url, '_blank'); + }} + {...attributes} + ref={ref} + className={`link-preview-block relative w-full cursor-pointer`} + > +
+ {data ? ( +
+ {data.title} +
+
{data.title}
+
{data.description}
+
+
+ ) : ( + node.data.url + )} +
+
+ {children} +
+
+ ); + }) +); +export default LinkPreview; diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/link-preview/index.ts b/frontend/appflowy_web_app/src/components/editor/components/blocks/link-preview/index.ts new file mode 100644 index 0000000000..67a3e2187a --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/link-preview/index.ts @@ -0,0 +1 @@ +export * from './LinkPreview'; diff --git a/frontend/appflowy_web_app/src/components/editor/components/element/Element.tsx b/frontend/appflowy_web_app/src/components/editor/components/element/Element.tsx index 2772ecd01b..49403021ac 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/element/Element.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/element/Element.tsx @@ -6,6 +6,7 @@ import { DatabaseBlock } from '@/components/editor/components/blocks/database'; import { DividerNode } from '@/components/editor/components/blocks/divider'; import { Heading } from '@/components/editor/components/blocks/heading'; import { ImageBlock } from '@/components/editor/components/blocks/image'; +import { LinkPreview } from '@/components/editor/components/blocks/link-preview'; import { MathEquation } from '@/components/editor/components/blocks/math-equation'; import { NumberedList } from '@/components/editor/components/blocks/numbered-list'; import { Outline } from '@/components/editor/components/blocks/outline'; @@ -74,6 +75,8 @@ export const Element = memo( case BlockType.BoardBlock: case BlockType.CalendarBlock: return DatabaseBlock; + case BlockType.LinkPreview: + return LinkPreview; default: return UnSupportedBlock; } diff --git a/frontend/appflowy_web_app/src/components/editor/editor.type.ts b/frontend/appflowy_web_app/src/components/editor/editor.type.ts index d21f75cd3a..eea2c5c329 100644 --- a/frontend/appflowy_web_app/src/components/editor/editor.type.ts +++ b/frontend/appflowy_web_app/src/components/editor/editor.type.ts @@ -17,6 +17,7 @@ import { BlockId, BlockData, DatabaseNodeData, + LinkPreviewBlockData, } from '@/application/collab.type'; import { HTMLAttributes } from 'react'; import { Element } from 'slate'; @@ -91,6 +92,12 @@ export interface CalloutNode extends BlockNode { data: CalloutBlockData; } +export interface LinkPreviewNode extends BlockNode { + type: BlockType.LinkPreview; + blockId: string; + data: LinkPreviewBlockData; +} + export interface MathEquationNode extends BlockNode { type: BlockType.EquationBlock; blockId: string;