import copy from "copy-to-clipboard"; import dayjs from "dayjs"; import { memo, useEffect, useRef, useState } from "react"; import { toast } from "react-hot-toast"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { useEditorStore, useLocationStore, useMemoStore, useUserStore } from "../store/module"; import Icon from "./Icon"; import MemoContent from "./MemoContent"; import MemoResources from "./MemoResources"; import showShareMemo from "./ShareMemoDialog"; import showPreviewImageDialog from "./PreviewImageDialog"; import showEmbedMemoDialog from "./EmbedMemoDialog"; import showChangeMemoCreatedTsDialog from "./ChangeMemoCreatedTsDialog"; import "../less/memo.less"; interface Props { memo: Memo; readonly?: boolean; } export const getFormatedMemoTimeStr = (time: number, locale = "en"): string => { if (Date.now() - time < 1000 * 60 * 60 * 24) { return dayjs(time).locale(locale).fromNow(); } else { return dayjs(time).locale(locale).format("YYYY/MM/DD HH:mm:ss"); } }; const Memo: React.FC = (props: Props) => { const { memo, readonly } = props; const { t, i18n } = useTranslation(); const navigate = useNavigate(); const editorStore = useEditorStore(); const locationStore = useLocationStore(); const userStore = useUserStore(); const memoStore = useMemoStore(); const [createdTimeStr, setCreatedTimeStr] = useState(getFormatedMemoTimeStr(memo.createdTs, i18n.language)); const memoContainerRef = useRef(null); const isVisitorMode = userStore.isVisitorMode() || readonly; useEffect(() => { let intervalFlag: any = -1; if (Date.now() - memo.createdTs < 1000 * 60 * 60 * 24) { intervalFlag = setInterval(() => { setCreatedTimeStr(getFormatedMemoTimeStr(memo.createdTs, i18n.language)); }, 1000 * 1); } return () => { clearInterval(intervalFlag); }; }, [i18n.language]); const handleViewMemoDetailPage = () => { navigate(`/m/${memo.id}`); }; const handleShowEmbedMemoDialog = () => { showEmbedMemoDialog(memo.id); }; const handleCopyContent = () => { copy(memo.content); toast.success(t("message.succeed-copy-content")); }; const handleTogglePinMemoBtnClick = async () => { if (isVisitorMode) { return; } try { if (memo.pinned) { await memoStore.unpinMemo(memo.id); } else { await memoStore.pinMemo(memo.id); } } catch (error) { // do nth } }; const handleEditMemoClick = () => { if (isVisitorMode) { return; } editorStore.setEditMemoWithId(memo.id); }; const handleArchiveMemoClick = async () => { if (isVisitorMode) { return; } try { await memoStore.patchMemo({ id: memo.id, rowStatus: "ARCHIVED", }); } catch (error: any) { console.error(error); toast.error(error.response.data.message); } if (editorStore.getState().editMemoId === memo.id) { editorStore.clearEditMemo(); } }; const handleGenerateMemoImageBtnClick = () => { showShareMemo(memo); }; const handleMemoContentClick = async (e: React.MouseEvent) => { const targetEl = e.target as HTMLElement; if (targetEl.className === "tag-span") { const tagName = targetEl.innerText.slice(1); const currTagQuery = locationStore.getState().query?.tag; if (currTagQuery === tagName) { locationStore.setTagQuery(undefined); } else { locationStore.setTagQuery(tagName); } } else if (targetEl.classList.contains("todo-block")) { if (isVisitorMode) { return; } const status = targetEl.dataset?.value; const todoElementList = [...(memoContainerRef.current?.querySelectorAll(`span.todo-block[data-value=${status}]`) ?? [])]; for (const element of todoElementList) { if (element === targetEl) { const index = todoElementList.indexOf(element); const tempList = memo.content.split(status === "DONE" ? /- \[x\] / : /- \[ \] /); 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 memoStore.patchMemo({ id: memo.id, content: finalContent, }); } } } else if (targetEl.tagName === "IMG") { const imgUrl = targetEl.getAttribute("src"); if (imgUrl) { showPreviewImageDialog([imgUrl], 0); } } }; const handleMemoContentDoubleClick = (e: React.MouseEvent) => { if (isVisitorMode) { return; } const loginUser = userStore.state.user; if (loginUser && !loginUser.localSetting.enableDoubleClickEditing) { return; } const targetEl = e.target as HTMLElement; if (targetEl.className === "tag-span") { return; } else if (targetEl.classList.contains("todo-block")) { return; } editorStore.setEditMemoWithId(memo.id); }; const handleMemoCreatedTimeClick = () => { showChangeMemoCreatedTsDialog(memo.id); }; const handleMemoVisibilityClick = (visibility: Visibility) => { const currVisibilityQuery = locationStore.getState().query?.visibility; if (currVisibilityQuery === visibility) { locationStore.setMemoVisibilityQuery(undefined); } else { locationStore.setMemoVisibilityQuery(visibility); } }; return (
{memo.pinned &&
}
{createdTimeStr} {isVisitorMode && ( @{memo.creatorName} )} {memo.visibility !== "PRIVATE" && !isVisitorMode && ( handleMemoVisibilityClick(memo.visibility)} > {memo.visibility} )}
{!isVisitorMode && (
{memo.pinned ? t("common.unpin") : t("common.pin")}
{t("common.edit")}
{t("common.share")}
{t("memo.copy")} {t("memo.view-detail")} Embed memo {t("common.archive")}
)}
); }; export default memo(Memo);