From df7b4d54c623dc31c4bfdae9e4530f0e6d5d6918 Mon Sep 17 00:00:00 2001 From: boojack <imrealleonardo@gmail.com> Date: Fri, 29 Jul 2022 20:11:14 +0800 Subject: [PATCH] chore: show inline image in daily review dialog (#135) --- web/src/components/ArchivedMemo.tsx | 2 +- web/src/components/DailyMemo.tsx | 21 ++++++------- web/src/components/Memo.tsx | 17 ++--------- web/src/components/MemoCardDialog.tsx | 3 +- web/src/components/ShareMemoImageDialog.tsx | 2 +- web/src/helpers/marked.ts | 33 ++++++++++++++++++++- web/src/less/memo-content.less | 4 +++ 7 files changed, 51 insertions(+), 31 deletions(-) diff --git a/web/src/components/ArchivedMemo.tsx b/web/src/components/ArchivedMemo.tsx index 776b19d8..1ec274d3 100644 --- a/web/src/components/ArchivedMemo.tsx +++ b/web/src/components/ArchivedMemo.tsx @@ -2,10 +2,10 @@ import { IMAGE_URL_REG } from "../helpers/consts"; import * as utils from "../helpers/utils"; import useToggle from "../hooks/useToggle"; import { memoService } from "../services"; +import { formatMemoContent } from "../helpers/marked"; import Only from "./common/OnlyWhen"; import Image from "./Image"; import toastHelper from "./Toast"; -import { formatMemoContent } from "./Memo"; import "../less/memo.less"; interface Props { diff --git a/web/src/components/DailyMemo.tsx b/web/src/components/DailyMemo.tsx index 8f7978c5..6b7ad40f 100644 --- a/web/src/components/DailyMemo.tsx +++ b/web/src/components/DailyMemo.tsx @@ -1,7 +1,5 @@ -import { IMAGE_URL_REG } from "../helpers/consts"; import * as utils from "../helpers/utils"; -import Only from "./common/OnlyWhen"; -import { formatMemoContent } from "./Memo"; +import { formatMemoContent } from "../helpers/marked"; import "../less/daily-memo.less"; interface DailyMemo extends Memo { @@ -20,7 +18,6 @@ const DailyMemo: React.FC<Props> = (props: Props) => { createdAtStr: utils.getDateTimeString(propsMemo.createdTs), timeStr: utils.getTimeString(propsMemo.createdTs), }; - const imageUrls = Array.from(memo.content.match(IMAGE_URL_REG) ?? []).map((s) => s.replace(IMAGE_URL_REG, "$1")); return ( <div className="daily-memo-wrapper"> @@ -28,14 +25,14 @@ const DailyMemo: React.FC<Props> = (props: Props) => { <span className="normal-text">{memo.timeStr}</span> </div> <div className="memo-content-container"> - <div className="memo-content-text" dangerouslySetInnerHTML={{ __html: formatMemoContent(memo.content) }}></div> - <Only when={imageUrls.length > 0}> - <div className="images-container"> - {imageUrls.map((imgUrl, idx) => ( - <img key={idx} src={imgUrl} decoding="async" /> - ))} - </div> - </Only> + <div + className="memo-content-text" + dangerouslySetInnerHTML={{ + __html: formatMemoContent(memo.content, { + inlineImage: true, + }), + }} + ></div> </div> <div className="split-line"></div> </div> diff --git a/web/src/components/Memo.tsx b/web/src/components/Memo.tsx index 69d4a3e5..55c18b1a 100644 --- a/web/src/components/Memo.tsx +++ b/web/src/components/Memo.tsx @@ -1,9 +1,9 @@ import { memo, useEffect, useRef, useState } from "react"; -import { escape, indexOf } from "lodash-es"; +import { indexOf } from "lodash-es"; import dayjs from "dayjs"; import relativeTime from "dayjs/plugin/relativeTime"; -import { IMAGE_URL_REG, LINK_URL_REG, MEMO_LINK_REG, TAG_REG, UNKNOWN_ID } from "../helpers/consts"; -import { DONE_BLOCK_REG, parseMarkedToHtml, TODO_BLOCK_REG } from "../helpers/marked"; +import { IMAGE_URL_REG, UNKNOWN_ID } from "../helpers/consts"; +import { DONE_BLOCK_REG, formatMemoContent, TODO_BLOCK_REG } from "../helpers/marked"; import { editorStateService, locationService, memoService, userService } from "../services"; import Only from "./common/OnlyWhen"; import toastHelper from "./Toast"; @@ -234,15 +234,4 @@ const Memo: React.FC<Props> = (props: Props) => { ); }; -export function formatMemoContent(content: string) { - const tempElement = document.createElement("div"); - tempElement.innerHTML = parseMarkedToHtml(escape(content)); - - return tempElement.innerHTML - .replace(IMAGE_URL_REG, "") - .replace(MEMO_LINK_REG, "<span class='memo-link-text' data-value='$2'>$1</span>") - .replace(LINK_URL_REG, "<a class='link' target='_blank' rel='noreferrer' href='$2'>$1</a>") - .replace(TAG_REG, "<span class='tag-span'>#$1</span> "); -} - export default memo(Memo); diff --git a/web/src/components/MemoCardDialog.tsx b/web/src/components/MemoCardDialog.tsx index 60065e70..edc7ee0d 100644 --- a/web/src/components/MemoCardDialog.tsx +++ b/web/src/components/MemoCardDialog.tsx @@ -2,12 +2,11 @@ import { useState, useEffect, useCallback } from "react"; import { editorStateService, memoService, userService } from "../services"; import { IMAGE_URL_REG, MEMO_LINK_REG, UNKNOWN_ID } from "../helpers/consts"; import * as utils from "../helpers/utils"; -import { parseHtmlToRawText } from "../helpers/marked"; +import { formatMemoContent, parseHtmlToRawText } from "../helpers/marked"; import Only from "./common/OnlyWhen"; import toastHelper from "./Toast"; import { generateDialog } from "./Dialog"; import Image from "./Image"; -import { formatMemoContent } from "./Memo"; import "../less/memo-card-dialog.less"; import Selector from "./common/Selector"; diff --git a/web/src/components/ShareMemoImageDialog.tsx b/web/src/components/ShareMemoImageDialog.tsx index 49bdc81f..69d7f82b 100644 --- a/web/src/components/ShareMemoImageDialog.tsx +++ b/web/src/components/ShareMemoImageDialog.tsx @@ -3,10 +3,10 @@ import { userService } from "../services"; import toImage from "../labs/html2image"; import { ANIMATION_DURATION, IMAGE_URL_REG } from "../helpers/consts"; import * as utils from "../helpers/utils"; +import { formatMemoContent } from "../helpers/marked"; import { generateDialog } from "./Dialog"; import Only from "./common/OnlyWhen"; import toastHelper from "./Toast"; -import { formatMemoContent } from "./Memo"; import "../less/share-memo-image-dialog.less"; interface Props extends DialogProps { diff --git a/web/src/helpers/marked.ts b/web/src/helpers/marked.ts index 569e1cd9..3f8f55f1 100644 --- a/web/src/helpers/marked.ts +++ b/web/src/helpers/marked.ts @@ -1,3 +1,6 @@ +import { escape } from "lodash-es"; +import { IMAGE_URL_REG, LINK_URL_REG, MEMO_LINK_REG, TAG_REG } from "./consts"; + const CODE_BLOCK_REG = /```([\s\S]*?)```/g; const BOLD_TEXT_REG = /\*\*(.+?)\*\*/g; const EM_TEXT_REG = /\*(.+?)\*/g; @@ -28,4 +31,32 @@ const parseHtmlToRawText = (htmlStr: string): string => { return text; }; -export { parseMarkedToHtml, parseHtmlToRawText }; +interface FormatterConfig { + inlineImage: boolean; +} +const defaultFormatterConfig: FormatterConfig = { + inlineImage: false, +}; + +const formatMemoContent = (content: string, addtionConfig?: Partial<FormatterConfig>) => { + const config = { + ...defaultFormatterConfig, + ...addtionConfig, + }; + const tempElement = document.createElement("div"); + tempElement.innerHTML = parseMarkedToHtml(escape(content)); + + let outputString = tempElement.innerHTML; + if (config.inlineImage) { + outputString = outputString.replace(IMAGE_URL_REG, "<img class='img' src='$1' />"); + } else { + outputString = outputString.replace(IMAGE_URL_REG, ""); + } + + return outputString + .replace(MEMO_LINK_REG, "<span class='memo-link-text' data-value='$2'>$1</span>") + .replace(LINK_URL_REG, "<a class='link' target='_blank' rel='noreferrer' href='$2'>$1</a>") + .replace(TAG_REG, "<span class='tag-span'>#$1</span> "); +}; + +export { formatMemoContent, parseHtmlToRawText }; diff --git a/web/src/less/memo-content.less b/web/src/less/memo-content.less index 2c39cf91..3612c386 100644 --- a/web/src/less/memo-content.less +++ b/web/src/less/memo-content.less @@ -7,6 +7,10 @@ @apply inline-block w-full h-auto mb-1 last:mb-0 text-base leading-7 whitespace-pre-wrap break-all; } + .img { + @apply float-left max-w-full w-full; + } + .tag-span { @apply inline-block w-auto font-mono text-blue-600; }