feat: memo relation part1 (#1677)

* feat: memo relation part1

* chore: update
This commit is contained in:
boojack
2023-05-18 21:29:28 +08:00
committed by GitHub
parent ca5859296a
commit a07d5d38d6
24 changed files with 425 additions and 207 deletions

View File

@ -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>
);
})}
</>
)}
</>
);
};