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 ( + + + + + + + + {showPinned && ( + + {memo.pinned ? : } + {memo.pinned ? t("common.unpin") : t("common.pin")} + + )} + + + {t("common.edit")} + + showShareMemoDialog(memo)}> + + {t("common.share")} + + + + + {t("common.archive")} + + + + {t("common.delete")} + + +
+
+ ID: {memo.name} +
+
+
+
+ ); +}; + +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")} - - -
-
- ID: {memo.name} -
-
-
-
- - )} + {!readonly && }
{ return ( setOpen(isOpen)}> - - - + + + diff --git a/web/src/components/ReactionView.tsx b/web/src/components/ReactionView.tsx index 24579781..2c371716 100644 --- a/web/src/components/ReactionView.tsx +++ b/web/src/components/ReactionView.tsx @@ -96,7 +96,7 @@ const ReactionView = (props: Props) => { className={classNames( "h-7 border px-2 py-0.5 rounded-full font-memo flex flex-row justify-center items-center gap-1 dark:border-zinc-700", currenUser && "cursor-pointer", - hasReaction && "bg-blue-50 border-blue-100 dark:bg-zinc-900", + hasReaction && "bg-blue-100 border-blue-200 dark:bg-zinc-900", )} onClick={handleReactionClick} > diff --git a/web/src/components/VisibilityIcon.tsx b/web/src/components/VisibilityIcon.tsx index dfbd8a89..13050e18 100644 --- a/web/src/components/VisibilityIcon.tsx +++ b/web/src/components/VisibilityIcon.tsx @@ -21,7 +21,7 @@ const VisibilityIcon = (props: Props) => { return null; } - return ; + return ; }; export default VisibilityIcon; diff --git a/web/src/less/memo.less b/web/src/less/memo.less deleted file mode 100644 index e15ab9eb..00000000 --- a/web/src/less/memo.less +++ /dev/null @@ -1,68 +0,0 @@ -.memo-wrapper { - @apply relative flex flex-col justify-start items-start w-full p-4 pt-3 mb-2 bg-white dark:bg-zinc-800 rounded-lg border border-white dark:border-zinc-800 hover:border-gray-200 dark:hover:border-zinc-700; - - &.pinned { - @apply border-gray-200 border dark:border-zinc-700; - } - - > .memo-top-wrapper { - @apply flex flex-row justify-between items-center w-full h-6; - - > .btns-container { - @apply flex flex-row justify-end items-center relative shrink-0; - - > .more-action-btns-wrapper { - @apply hidden flex-col justify-start items-center absolute top-2 -right-4 flex-nowrap hover:flex p-3; - - > .more-action-btns-container { - @apply w-auto h-auto p-1 z-1 whitespace-nowrap rounded-lg bg-white dark:bg-zinc-700; - box-shadow: 0 0 8px 0 rgb(0 0 0 / 20%); - - > .btn { - @apply w-full text-sm leading-6 py-1 px-3 rounded justify-start cursor-pointer select-none dark:text-gray-300; - - &.archive-btn { - @apply text-orange-600; - } - } - } - } - - .btn { - @apply flex flex-row justify-center items-center leading-6 text-sm rounded hover:bg-gray-200 dark:hover:bg-zinc-600; - - &.more-action-btn { - @apply w-auto opacity-50 cursor-default hover:bg-transparent; - - > .icon-img { - @apply w-4 h-auto dark:text-gray-300; - } - - &:hover { - & + .more-action-btns-wrapper { - display: flex; - } - } - } - } - - .final-confirm { - color: red; - } - } - } - - > .memo-content-wrapper { - @apply mt-1; - - > .memo-content-text { - .tag-span { - @apply cursor-pointer hover:opacity-80; - } - - .img { - @apply max-w-xs; - } - } - } -}