mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
feat: memo relation part1 (#1677)
* feat: memo relation part1 * chore: update
This commit is contained in:
@ -1,9 +1,12 @@
|
||||
import { getRelativeTimeString } from "@/helpers/datetime";
|
||||
import { isEqual, uniqWith } from "lodash-es";
|
||||
import { memo, useEffect, useRef, useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useEditorStore, useFilterStore, useMemoStore, useUserStore } from "@/store/module";
|
||||
import { getRelativeTimeString } from "@/helpers/datetime";
|
||||
import { UNKNOWN_ID } from "@/helpers/consts";
|
||||
import { useMemoCacheStore } from "@/store/zustand";
|
||||
import Tooltip from "./kit/Tooltip";
|
||||
import Divider from "./kit/Divider";
|
||||
import { showCommonDialog } from "./Dialog/CommonDialog";
|
||||
@ -13,24 +16,34 @@ import MemoResources from "./MemoResources";
|
||||
import showShareMemo from "./ShareMemoDialog";
|
||||
import showPreviewImageDialog from "./PreviewImageDialog";
|
||||
import showChangeMemoCreatedTsDialog from "./ChangeMemoCreatedTsDialog";
|
||||
import MemoRelationListView from "./MemoRelationListView";
|
||||
import "@/less/memo.less";
|
||||
|
||||
interface Props {
|
||||
memo: Memo;
|
||||
readonly?: boolean;
|
||||
showRelatedMemos?: boolean;
|
||||
}
|
||||
|
||||
const Memo: React.FC<Props> = (props: Props) => {
|
||||
const { memo, readonly } = props;
|
||||
const { memo, readonly, showRelatedMemos } = props;
|
||||
const { t, i18n } = useTranslation();
|
||||
const editorStore = useEditorStore();
|
||||
const filterStore = useFilterStore();
|
||||
const userStore = useUserStore();
|
||||
const memoStore = useMemoStore();
|
||||
const memoCacheStore = useMemoCacheStore();
|
||||
const [createdTimeStr, setCreatedTimeStr] = useState<string>(getRelativeTimeString(memo.createdTs));
|
||||
const [relatedMemoList, setRelatedMemoList] = useState<Memo[]>([]);
|
||||
const memoContainerRef = useRef<HTMLDivElement>(null);
|
||||
const isVisitorMode = userStore.isVisitorMode() || readonly;
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all(memo.relationList.map((memoRelation) => memoCacheStore.getOrFetchMemoById(memoRelation.relatedMemoId))).then((memoList) => {
|
||||
setRelatedMemoList(uniqWith(memoList, isEqual));
|
||||
});
|
||||
}, [memo.relationList]);
|
||||
|
||||
useEffect(() => {
|
||||
let intervalFlag: any = -1;
|
||||
if (Date.now() - memo.createdTs < 1000 * 60 * 60 * 24) {
|
||||
@ -178,71 +191,106 @@ const Memo: React.FC<Props> = (props: Props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleMarkMemo = () => {
|
||||
const relation: MemoRelation = {
|
||||
memoId: UNKNOWN_ID,
|
||||
relatedMemoId: memo.id,
|
||||
type: "REFERENCE",
|
||||
};
|
||||
editorStore.setRelationList(uniqWith([...editorStore.state.relationList, relation], isEqual));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`memo-wrapper ${"memos-" + memo.id} ${memo.pinned ? "pinned" : ""}`} ref={memoContainerRef}>
|
||||
<div className="memo-top-wrapper">
|
||||
<div className="status-text-container">
|
||||
<Link className="time-text" to={`/m/${memo.id}`} onClick={handleMemoCreatedTimeClick}>
|
||||
{createdTimeStr}
|
||||
</Link>
|
||||
{isVisitorMode && (
|
||||
<Link className="name-text" to={`/u/${memo.creatorId}`}>
|
||||
@{memo.creatorName}
|
||||
<>
|
||||
<div
|
||||
className={`memo-wrapper ${"memos-" + memo.id} ${relatedMemoList.length > 0 && "pinned"} ${memo.pinned ? "pinned" : ""}`}
|
||||
ref={memoContainerRef}
|
||||
>
|
||||
<div className="memo-top-wrapper">
|
||||
<div className="status-text-container">
|
||||
<Link className="time-text" to={`/m/${memo.id}`} onClick={handleMemoCreatedTimeClick}>
|
||||
{createdTimeStr}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
{!isVisitorMode && (
|
||||
<div className="btns-container space-x-2">
|
||||
{memo.visibility !== "PRIVATE" && (
|
||||
<Tooltip title={t(`memo.visibility.${memo.visibility.toLowerCase()}`)} side="top">
|
||||
<div onClick={() => handleMemoVisibilityClick(memo.visibility)}>
|
||||
{memo.visibility === "PUBLIC" ? (
|
||||
<Icon.Globe2 className="w-4 h-auto cursor-pointer rounded text-green-600" />
|
||||
) : (
|
||||
<Icon.Users className="w-4 h-auto cursor-pointer rounded text-gray-500 dark:text-gray-400" />
|
||||
)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
{isVisitorMode && (
|
||||
<Link className="name-text" to={`/u/${memo.creatorId}`}>
|
||||
@{memo.creatorName}
|
||||
</Link>
|
||||
)}
|
||||
{memo.pinned && <Icon.Bookmark className="w-4 h-auto rounded text-green-600" />}
|
||||
<span className="btn more-action-btn">
|
||||
<Icon.MoreHorizontal className="icon-img" />
|
||||
</span>
|
||||
<div className="more-action-btns-wrapper">
|
||||
<div className="more-action-btns-container min-w-[6em]">
|
||||
<span className="btn" onClick={handleTogglePinMemoBtnClick}>
|
||||
{memo.pinned ? <Icon.BookmarkMinus className="w-4 h-auto mr-2" /> : <Icon.BookmarkPlus className="w-4 h-auto mr-2" />}
|
||||
{memo.pinned ? t("common.unpin") : t("common.pin")}
|
||||
</span>
|
||||
<span className="btn" onClick={handleEditMemoClick}>
|
||||
<Icon.Edit3 className="w-4 h-auto mr-2" />
|
||||
{t("common.edit")}
|
||||
</span>
|
||||
<span className="btn" onClick={handleGenerateMemoImageBtnClick}>
|
||||
<Icon.Share className="w-4 h-auto mr-2" />
|
||||
{t("common.share")}
|
||||
</span>
|
||||
<Divider />
|
||||
<span className="btn text-orange-500" onClick={handleArchiveMemoClick}>
|
||||
<Icon.Archive className="w-4 h-auto mr-2" />
|
||||
{t("common.archive")}
|
||||
</span>
|
||||
<span className="btn text-red-600" onClick={handleDeleteMemoClick}>
|
||||
<Icon.Trash className="w-4 h-auto mr-2" />
|
||||
{t("common.delete")}
|
||||
</span>
|
||||
</div>
|
||||
{!isVisitorMode && (
|
||||
<div className="btns-container space-x-2">
|
||||
{memo.visibility !== "PRIVATE" && (
|
||||
<Tooltip title={t(`memo.visibility.${memo.visibility.toLowerCase()}`)} side="top">
|
||||
<div onClick={() => handleMemoVisibilityClick(memo.visibility)}>
|
||||
{memo.visibility === "PUBLIC" ? (
|
||||
<Icon.Globe2 className="w-4 h-auto cursor-pointer rounded text-green-600" />
|
||||
) : (
|
||||
<Icon.Users className="w-4 h-auto cursor-pointer rounded text-gray-500 dark:text-gray-400" />
|
||||
)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
{memo.pinned && <Icon.Bookmark className="w-4 h-auto rounded text-green-600" />}
|
||||
<span className="btn more-action-btn">
|
||||
<Icon.MoreHorizontal className="icon-img" />
|
||||
</span>
|
||||
<div className="more-action-btns-wrapper">
|
||||
<div className="more-action-btns-container min-w-[6em]">
|
||||
<span className="btn" onClick={handleTogglePinMemoBtnClick}>
|
||||
{memo.pinned ? <Icon.BookmarkMinus className="w-4 h-auto mr-2" /> : <Icon.BookmarkPlus className="w-4 h-auto mr-2" />}
|
||||
{memo.pinned ? t("common.unpin") : t("common.pin")}
|
||||
</span>
|
||||
<span className="btn" onClick={handleEditMemoClick}>
|
||||
<Icon.Edit3 className="w-4 h-auto mr-2" />
|
||||
{t("common.edit")}
|
||||
</span>
|
||||
<span className="btn" onClick={handleGenerateMemoImageBtnClick}>
|
||||
<Icon.Share className="w-4 h-auto mr-2" />
|
||||
{t("common.share")}
|
||||
</span>
|
||||
<span className="btn" onClick={handleMarkMemo}>
|
||||
<Icon.Link className="w-4 h-auto mr-2" />
|
||||
Mark
|
||||
</span>
|
||||
<Divider />
|
||||
<span className="btn text-orange-500" onClick={handleArchiveMemoClick}>
|
||||
<Icon.Archive className="w-4 h-auto mr-2" />
|
||||
{t("common.archive")}
|
||||
</span>
|
||||
<span className="btn text-red-600" onClick={handleDeleteMemoClick}>
|
||||
<Icon.Trash className="w-4 h-auto mr-2" />
|
||||
{t("common.delete")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
<MemoContent
|
||||
content={memo.content}
|
||||
onMemoContentClick={handleMemoContentClick}
|
||||
onMemoContentDoubleClick={handleMemoContentDoubleClick}
|
||||
/>
|
||||
<MemoResources resourceList={memo.resourceList} />
|
||||
{!showRelatedMemos && <MemoRelationListView relationList={memo.relationList} />}
|
||||
</div>
|
||||
<MemoContent
|
||||
content={memo.content}
|
||||
onMemoContentClick={handleMemoContentClick}
|
||||
onMemoContentDoubleClick={handleMemoContentDoubleClick}
|
||||
/>
|
||||
<MemoResources resourceList={memo.resourceList} />
|
||||
</div>
|
||||
|
||||
{showRelatedMemos && relatedMemoList.length > 0 && (
|
||||
<>
|
||||
<p className="font-mono text-sm mt-4 mb-1 pl-4 opacity-60 flex flex-row items-center">
|
||||
<Icon.Link className="w-4 h-auto mr-1" />
|
||||
<span>Related memos</span>
|
||||
</p>
|
||||
{relatedMemoList.map((relatedMemo) => {
|
||||
return (
|
||||
<div key={relatedMemo.id} className="w-full">
|
||||
<Memo memo={relatedMemo} readonly={readonly} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user