diff --git a/web/src/components/MemoActionMenu.tsx b/web/src/components/MemoActionMenu.tsx
new file mode 100644
index 00000000..939002c2
--- /dev/null
+++ b/web/src/components/MemoActionMenu.tsx
@@ -0,0 +1,128 @@
+import { Divider, Dropdown, Menu, MenuButton, MenuItem } from "@mui/joy";
+import copy from "copy-to-clipboard";
+import toast from "react-hot-toast";
+import Icon from "@/components/Icon";
+import { useMemoStore } from "@/store/v1";
+import { RowStatus } from "@/types/proto/api/v2/common";
+import { Memo } from "@/types/proto/api/v2/memo_service";
+import { useTranslate } from "@/utils/i18n";
+import { showCommonDialog } from "./Dialog/CommonDialog";
+import showMemoEditorDialog from "./MemoEditor/MemoEditorDialog";
+import showShareMemoDialog from "./ShareMemoDialog";
+
+interface Props {
+ memo: Memo;
+ showPinned?: boolean;
+}
+
+const MemoActionMenu = (props: Props) => {
+ const { memo, showPinned } = props;
+ const t = useTranslate();
+ const memoStore = useMemoStore();
+
+ const handleTogglePinMemoBtnClick = async () => {
+ try {
+ if (memo.pinned) {
+ await memoStore.updateMemo(
+ {
+ id: memo.id,
+ pinned: false,
+ },
+ ["pinned"],
+ );
+ } else {
+ await memoStore.updateMemo(
+ {
+ id: memo.id,
+ pinned: true,
+ },
+ ["pinned"],
+ );
+ }
+ } catch (error) {
+ // do nth
+ }
+ };
+
+ const handleEditMemoClick = () => {
+ showMemoEditorDialog({
+ memoId: memo.id,
+ cacheKey: `${memo.id}-${memo.updateTime}`,
+ });
+ };
+
+ const handleArchiveMemoClick = async () => {
+ try {
+ await memoStore.updateMemo(
+ {
+ id: memo.id,
+ rowStatus: RowStatus.ARCHIVED,
+ },
+ ["row_status"],
+ );
+ } catch (error: any) {
+ console.error(error);
+ toast.error(error.response.data.message);
+ }
+ };
+
+ const handleDeleteMemoClick = async () => {
+ showCommonDialog({
+ title: t("memo.delete-memo"),
+ content: t("memo.delete-confirm"),
+ style: "danger",
+ dialogName: "delete-memo-dialog",
+ onConfirm: async () => {
+ await memoStore.deleteMemo(memo.id);
+ },
+ });
+ };
+
+ const handleCopyMemoId = () => {
+ copy(memo.name);
+ toast.success("Copied to clipboard!");
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+
+export default MemoActionMenu;
diff --git a/web/src/components/MemoReactionListView.tsx b/web/src/components/MemoReactionListView.tsx
index 10452e5d..d117cb5e 100644
--- a/web/src/components/MemoReactionListView.tsx
+++ b/web/src/components/MemoReactionListView.tsx
@@ -33,12 +33,12 @@ const MemoReactionListView = (props: Props) => {
}, [reactions]);
return (
- (currentUser || reactionGroup.size > 0) && (
+ reactions.length > 0 && (
- {currentUser && }
{Array.from(reactionGroup).map(([reactionType, users]) => {
return ;
})}
+ {currentUser && }
)
);
diff --git a/web/src/components/MemoView.tsx b/web/src/components/MemoView.tsx
index 89320c85..2e0aacb0 100644
--- a/web/src/components/MemoView.tsx
+++ b/web/src/components/MemoView.tsx
@@ -1,33 +1,27 @@
-import { Divider, Tooltip } from "@mui/joy";
+import { Tooltip } from "@mui/joy";
import classNames from "classnames";
-import copy from "copy-to-clipboard";
import { memo, useCallback, useEffect, useRef, useState } from "react";
-import { toast } from "react-hot-toast";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
-import { UNKNOWN_ID } from "@/helpers/consts";
import { getRelativeTimeString, getTimeStampByDate } from "@/helpers/datetime";
import useCurrentUser from "@/hooks/useCurrentUser";
import useNavigateTo from "@/hooks/useNavigateTo";
-import { useUserStore, extractUsernameFromName, useMemoStore } from "@/store/v1";
-import { RowStatus } from "@/types/proto/api/v2/common";
+import { useUserStore, extractUsernameFromName } from "@/store/v1";
import { MemoRelation_Type } from "@/types/proto/api/v2/memo_relation_service";
import { Memo, Visibility } from "@/types/proto/api/v2/memo_service";
import { useTranslate } from "@/utils/i18n";
import { convertVisibilityToString } from "@/utils/memo";
import showChangeMemoCreatedTsDialog from "./ChangeMemoCreatedTsDialog";
-import { showCommonDialog } from "./Dialog/CommonDialog";
import Icon from "./Icon";
+import MemoActionMenu from "./MemoActionMenu";
import MemoContent from "./MemoContent";
-import showMemoEditorDialog from "./MemoEditor/MemoEditorDialog";
import MemoReactionistView from "./MemoReactionListView";
import MemoRelationListView from "./MemoRelationListView";
import MemoResourceListView from "./MemoResourceListView";
import showPreviewImageDialog from "./PreviewImageDialog";
-import showShareMemoDialog from "./ShareMemoDialog";
+import ReactionSelector from "./ReactionSelector";
import UserAvatar from "./UserAvatar";
import VisibilityIcon from "./VisibilityIcon";
-import "@/less/memo.less";
interface Props {
memo: Memo;
@@ -42,7 +36,7 @@ const MemoView: React.FC = (props: Props) => {
const t = useTranslate();
const navigateTo = useNavigateTo();
const { i18n } = useTranslation();
- const memoStore = useMemoStore();
+ const currentUser = useCurrentUser();
const userStore = useUserStore();
const user = useCurrentUser();
const [displayTime, setDisplayTime] = useState(getRelativeTimeString(getTimeStampByDate(memo.displayTime)));
@@ -80,81 +74,6 @@ const MemoView: React.FC = (props: Props) => {
}
};
- const handleTogglePinMemoBtnClick = async () => {
- try {
- if (memo.pinned) {
- await memoStore.updateMemo(
- {
- id: memo.id,
- pinned: false,
- },
- ["pinned"],
- );
- } else {
- await memoStore.updateMemo(
- {
- id: memo.id,
- pinned: true,
- },
- ["pinned"],
- );
- }
- } catch (error) {
- // do nth
- }
- };
-
- const handleEditMemoClick = () => {
- showMemoEditorDialog({
- memoId: memo.id,
- cacheKey: `${memo.id}-${memo.updateTime}`,
- });
- };
-
- const handleMarkMemoClick = () => {
- showMemoEditorDialog({
- relationList: [
- {
- memoId: UNKNOWN_ID,
- relatedMemoId: memo.id,
- type: MemoRelation_Type.REFERENCE,
- },
- ],
- });
- };
-
- const handleArchiveMemoClick = async () => {
- try {
- await memoStore.updateMemo(
- {
- id: memo.id,
- rowStatus: RowStatus.ARCHIVED,
- },
- ["row_status"],
- );
- } catch (error: any) {
- console.error(error);
- toast.error(error.response.data.message);
- }
- };
-
- const handleDeleteMemoClick = async () => {
- showCommonDialog({
- title: t("memo.delete-memo"),
- content: t("memo.delete-confirm"),
- style: "danger",
- dialogName: "delete-memo-dialog",
- onConfirm: async () => {
- await memoStore.deleteMemo(memo.id);
- },
- });
- };
-
- const handleCopyMemoId = () => {
- copy(memo.name);
- toast.success("Copied to clipboard!");
- };
-
const handleMemoContentClick = useCallback(async (e: React.MouseEvent) => {
const targetEl = e.target as HTMLElement;
@@ -168,11 +87,16 @@ const MemoView: React.FC = (props: Props) => {
return (
-
-
+
+
{props.showCreator && creator && (
<>
@@ -200,62 +124,20 @@ const MemoView: React.FC
= (props: Props) => {
>
)}
-
-
+
+
{props.showVisibility && memo.visibility !== Visibility.PRIVATE && (
<>
-
+
>
)}
+ {currentUser && memo.reactions.length === 0 && }
- {!readonly && (
- <>
-
-
-
-
-
- {props.showPinned && (
-
- {memo.pinned ? : }
- {memo.pinned ? t("common.unpin") : t("common.pin")}
-
- )}
-
-
- {t("common.edit")}
-
-
-
- {t("common.mark")}
-
-
showShareMemoDialog(memo)}>
-
- {t("common.share")}
-
-
-
-
- {t("common.archive")}
-
-
-
- {t("common.delete")}
-
-
-
-
-
- >
- )}
+ {!readonly &&
}
{
return (
setOpen(isOpen)}>
-
-
-
+
+
+