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;
   }