import dayjs from "dayjs"; import relativeTime from "dayjs/plugin/relativeTime"; import { indexOf } from "lodash-es"; import { memo, useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import "dayjs/locale/zh"; import { UNKNOWN_ID } from "../helpers/consts"; import { DONE_BLOCK_REG, TODO_BLOCK_REG } from "../helpers/marked"; import { editorStateService, locationService, memoService, userService } from "../services"; import Icon from "./Icon"; import Only from "./common/OnlyWhen"; import toastHelper from "./Toast"; import MemoContent from "./MemoContent"; import MemoResources from "./MemoResources"; import showMemoCardDialog from "./MemoCardDialog"; import showShareMemoImageDialog from "./ShareMemoImageDialog"; import "../less/memo.less"; dayjs.extend(relativeTime); interface Props { memo: Memo; } export const getFormatedMemoCreatedAtStr = (createdTs: number, locale = "en"): string => { if (Date.now() - createdTs < 1000 * 60 * 60 * 24) { return dayjs(createdTs).locale(locale).fromNow(); } else { return dayjs(createdTs).locale(locale).format("YYYY/MM/DD HH:mm:ss"); } }; const Memo: React.FC = (props: Props) => { const memo = props.memo; const { t, i18n } = useTranslation(); const [createdAtStr, setCreatedAtStr] = useState(getFormatedMemoCreatedAtStr(memo.createdTs, i18n.language)); const memoContainerRef = useRef(null); const memoContentContainerRef = useRef(null); const isVisitorMode = userService.isVisitorMode(); useEffect(() => { let intervalFlag = -1; if (Date.now() - memo.createdTs < 1000 * 60 * 60 * 24) { intervalFlag = setInterval(() => { setCreatedAtStr(getFormatedMemoCreatedAtStr(memo.createdTs, i18n.language)); }, 1000 * 1); } return () => { clearInterval(intervalFlag); }; }, [i18n.language]); const handleShowMemoStoryDialog = () => { showMemoCardDialog(memo); }; const handleTogglePinMemoBtnClick = async () => { try { if (memo.pinned) { await memoService.unpinMemo(memo.id); } else { await memoService.pinMemo(memo.id); } } catch (error) { // do nth } }; const handleMarkMemoClick = () => { editorStateService.setMarkMemoWithId(memo.id); }; const handleEditMemoClick = () => { editorStateService.setEditMemoWithId(memo.id); }; const handleArchiveMemoClick = async () => { try { await memoService.patchMemo({ id: memo.id, rowStatus: "ARCHIVED", }); } catch (error: any) { console.error(error); toastHelper.error(error.response.data.message); } if (editorStateService.getState().editMemoId === memo.id) { editorStateService.clearEditMemo(); } }; 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(Number(memoId) ?? UNKNOWN_ID); if (memoTemp) { showMemoCardDialog(memoTemp); } else { toastHelper.error(t("message.memo-not-found")); targetEl.classList.remove("memo-link-text"); } } else if (targetEl.className === "tag-span") { const tagName = targetEl.innerText.slice(1); const currTagQuery = locationService.getState().query?.tag; if (currTagQuery === tagName) { locationService.setTagQuery(undefined); } else { locationService.setTagQuery(tagName); } } else if (targetEl.classList.contains("todo-block")) { if (userService.isVisitorMode()) { return; } const status = targetEl.dataset?.value; const todoElementList = [...(memoContentContainerRef.current?.querySelectorAll(`span.todo-block[data-value=${status}]`) ?? [])]; for (const element of todoElementList) { if (element === targetEl) { const index = indexOf(todoElementList, element); const tempList = memo.content.split(status === "DONE" ? DONE_BLOCK_REG : TODO_BLOCK_REG); let finalContent = ""; for (let i = 0; i < tempList.length; i++) { if (i === 0) { finalContent += `${tempList[i]}`; } else { if (i === index + 1) { finalContent += status === "DONE" ? "- [ ] " : "- [x] "; } else { finalContent += status === "DONE" ? "- [x] " : "- [ ] "; } finalContent += `${tempList[i]}`; } } await memoService.patchMemo({ id: memo.id, content: finalContent, }); } } } }; const handleMemoContentDoubleClick = (e: React.MouseEvent) => { const targetEl = e.target as HTMLElement; if (targetEl.className === "memo-link-text") { return; } else if (targetEl.className === "tag-span") { return; } else if (targetEl.classList.contains("todo-block")) { return; } editorStateService.setEditMemoWithId(memo.id); }; return (
{createdAtStr} {memo.visibility}
{memo.pinned ? t("common.unpin") : t("common.pin")}
{t("common.edit")}
{t("common.share")}
{t("common.mark")} {t("memo.view-story")} {t("common.archive")}
); }; export default memo(Memo);