mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
migrate frontend
This commit is contained in:
176
web/src/components/Memo.tsx
Normal file
176
web/src/components/Memo.tsx
Normal file
@ -0,0 +1,176 @@
|
||||
import { memo } from "react";
|
||||
import { IMAGE_URL_REG, LINK_REG, MEMO_LINK_REG, TAG_REG } from "../helpers/consts";
|
||||
import { encodeHtml, parseMarkedToHtml, parseRawTextToHtml } from "../helpers/marked";
|
||||
import utils from "../helpers/utils";
|
||||
import useToggle from "../hooks/useToggle";
|
||||
import { globalStateService, memoService } from "../services";
|
||||
import Only from "./common/OnlyWhen";
|
||||
import Image from "./Image";
|
||||
import showMemoCardDialog from "./MemoCardDialog";
|
||||
import showShareMemoImageDialog from "./ShareMemoImageDialog";
|
||||
import toastHelper from "./Toast";
|
||||
import "../less/memo.less";
|
||||
|
||||
interface Props {
|
||||
memo: Model.Memo;
|
||||
}
|
||||
|
||||
const Memo: React.FC<Props> = (props: Props) => {
|
||||
const { memo: propsMemo } = props;
|
||||
const memo: FormattedMemo = {
|
||||
...propsMemo,
|
||||
createdAtStr: utils.getDateTimeString(propsMemo.createdAt),
|
||||
};
|
||||
const [showConfirmDeleteBtn, toggleConfirmDeleteBtn] = useToggle(false);
|
||||
const imageUrls = Array.from(memo.content.match(IMAGE_URL_REG) ?? []);
|
||||
|
||||
const handleShowMemoStoryDialog = () => {
|
||||
showMemoCardDialog(memo);
|
||||
};
|
||||
|
||||
const handleMarkMemoClick = () => {
|
||||
globalStateService.setMarkMemoId(memo.id);
|
||||
};
|
||||
|
||||
const handleEditMemoClick = () => {
|
||||
globalStateService.setEditMemoId(memo.id);
|
||||
};
|
||||
|
||||
const handleDeleteMemoClick = async () => {
|
||||
if (showConfirmDeleteBtn) {
|
||||
try {
|
||||
await memoService.hideMemoById(memo.id);
|
||||
} catch (error: any) {
|
||||
toastHelper.error(error.message);
|
||||
}
|
||||
|
||||
if (globalStateService.getState().editMemoId === memo.id) {
|
||||
globalStateService.setEditMemoId("");
|
||||
}
|
||||
} else {
|
||||
toggleConfirmDeleteBtn();
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseLeaveMemoWrapper = () => {
|
||||
if (showConfirmDeleteBtn) {
|
||||
toggleConfirmDeleteBtn(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleGenMemoImageBtnClick = () => {
|
||||
showShareMemoImageDialog(memo);
|
||||
};
|
||||
|
||||
const handleMemoContentClick = async (e: React.MouseEvent) => {
|
||||
const targetEl = e.target as HTMLElement;
|
||||
|
||||
if (targetEl.className === "memo-link-text") {
|
||||
const memoId = targetEl.dataset?.value;
|
||||
const memoTemp = memoService.getMemoById(memoId ?? "");
|
||||
|
||||
if (memoTemp) {
|
||||
showMemoCardDialog(memoTemp);
|
||||
} else {
|
||||
toastHelper.error("MEMO Not Found");
|
||||
targetEl.classList.remove("memo-link-text");
|
||||
}
|
||||
} else if (targetEl.className === "todo-block") {
|
||||
// do nth
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`memo-wrapper ${"memos-" + memo.id}`} onMouseLeave={handleMouseLeaveMemoWrapper}>
|
||||
<div className="memo-top-wrapper">
|
||||
<span className="time-text" onClick={handleShowMemoStoryDialog}>
|
||||
{memo.createdAtStr}
|
||||
</span>
|
||||
<div className="btns-container">
|
||||
<span className="btn more-action-btn">
|
||||
<img className="icon-img" src="/icons/more.svg" />
|
||||
</span>
|
||||
<div className="more-action-btns-wrapper">
|
||||
<div className="more-action-btns-container">
|
||||
<span className="btn" onClick={handleShowMemoStoryDialog}>
|
||||
查看详情
|
||||
</span>
|
||||
<span className="btn" onClick={handleMarkMemoClick}>
|
||||
Mark
|
||||
</span>
|
||||
<span className="btn" onClick={handleGenMemoImageBtnClick}>
|
||||
分享
|
||||
</span>
|
||||
<span className="btn" onClick={handleEditMemoClick}>
|
||||
编辑
|
||||
</span>
|
||||
<span className={`btn delete-btn ${showConfirmDeleteBtn ? "final-confirm" : ""}`} onClick={handleDeleteMemoClick}>
|
||||
{showConfirmDeleteBtn ? "确定删除!" : "删除"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="memo-content-text"
|
||||
onClick={handleMemoContentClick}
|
||||
dangerouslySetInnerHTML={{ __html: formatMemoContent(memo.content) }}
|
||||
></div>
|
||||
<Only when={imageUrls.length > 0}>
|
||||
<div className="images-wrapper">
|
||||
{imageUrls.map((imgUrl, idx) => (
|
||||
<Image className="memo-img" key={idx} imgUrl={imgUrl} />
|
||||
))}
|
||||
</div>
|
||||
</Only>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export function formatMemoContent(content: string) {
|
||||
content = encodeHtml(content);
|
||||
content = parseRawTextToHtml(content)
|
||||
.split("<br>")
|
||||
.map((t) => {
|
||||
return `<p>${t !== "" ? t : "<br>"}</p>`;
|
||||
})
|
||||
.join("");
|
||||
|
||||
const { shouldUseMarkdownParser, shouldSplitMemoWord, shouldHideImageUrl } = globalStateService.getState();
|
||||
|
||||
if (shouldUseMarkdownParser) {
|
||||
content = parseMarkedToHtml(content);
|
||||
}
|
||||
|
||||
if (shouldHideImageUrl) {
|
||||
content = content.replace(IMAGE_URL_REG, "");
|
||||
}
|
||||
|
||||
// 中英文之间加空格
|
||||
if (shouldSplitMemoWord) {
|
||||
content = content
|
||||
.replace(/([\u4e00-\u9fa5])([A-Za-z0-9?.,;[\]]+)/g, "$1 $2")
|
||||
.replace(/([A-Za-z0-9?.,;[\]]+)([\u4e00-\u9fa5])/g, "$1 $2");
|
||||
}
|
||||
|
||||
content = content
|
||||
.replace(TAG_REG, "<span class='tag-span'>#$1</span>")
|
||||
.replace(LINK_REG, "<a class='link' target='_blank' rel='noreferrer' href='$1'>$1</a>")
|
||||
.replace(MEMO_LINK_REG, "<span class='memo-link-text' data-value='$2'>$1</span>");
|
||||
|
||||
const tempDivContainer = document.createElement("div");
|
||||
tempDivContainer.innerHTML = content;
|
||||
for (let i = 0; i < tempDivContainer.children.length; i++) {
|
||||
const c = tempDivContainer.children[i];
|
||||
|
||||
if (c.tagName === "P" && c.textContent === "" && c.firstElementChild?.tagName !== "BR") {
|
||||
c.remove();
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return tempDivContainer.innerHTML;
|
||||
}
|
||||
|
||||
export default memo(Memo);
|
Reference in New Issue
Block a user