From 21c70e799349d4b762857d357dc167666c2148ee Mon Sep 17 00:00:00 2001 From: Steven Date: Thu, 19 Oct 2023 00:18:07 +0800 Subject: [PATCH] feat: update memo relations dialog --- api/v1/memo.go | 6 +- .../components/CreateMemoRelationDialog.tsx | 134 ++++++++++++++++++ web/src/components/Dialog/BaseDialog.tsx | 8 +- web/src/components/Header.tsx | 4 +- web/src/components/Memo.tsx | 11 +- .../MemoEditor/MemoEditorDialog.tsx | 9 +- web/src/components/MemoEditor/index.tsx | 25 +++- web/src/components/MemoList.tsx | 2 +- web/src/less/base-dialog.less | 2 +- web/src/pages/MemoDetail.tsx | 4 + 10 files changed, 190 insertions(+), 15 deletions(-) create mode 100644 web/src/components/CreateMemoRelationDialog.tsx diff --git a/api/v1/memo.go b/api/v1/memo.go index 06138977..5c402991 100644 --- a/api/v1/memo.go +++ b/api/v1/memo.go @@ -194,9 +194,9 @@ func (s *APIV1Service) GetMemoList(c echo.Context) error { if tag != "" { contentSearch = append(contentSearch, "#"+tag) } - contentSlice := c.QueryParams()["content"] - if len(contentSlice) > 0 { - contentSearch = append(contentSearch, contentSlice...) + content := c.QueryParam("content") + if content != "" { + contentSearch = append(contentSearch, content) } findMemoMessage.ContentSearch = contentSearch diff --git a/web/src/components/CreateMemoRelationDialog.tsx b/web/src/components/CreateMemoRelationDialog.tsx new file mode 100644 index 00000000..fe251b45 --- /dev/null +++ b/web/src/components/CreateMemoRelationDialog.tsx @@ -0,0 +1,134 @@ +import { Button, Input } from "@mui/joy"; +import { isNaN, unionBy } from "lodash-es"; +import React, { useState } from "react"; +import { toast } from "react-hot-toast"; +import { memoServiceClient } from "@/grpcweb"; +import { Memo } from "@/types/proto/api/v2/memo_service"; +import { useTranslate } from "@/utils/i18n"; +import { generateDialog } from "./Dialog"; +import Icon from "./Icon"; + +interface Props extends DialogProps { + onCancel?: () => void; + onConfirm?: (memoIdList: number[]) => void; +} + +const CreateMemoRelationDialog: React.FC = (props: Props) => { + const { destroy, onCancel, onConfirm } = props; + const t = useTranslate(); + const [memoId, setMemoId] = useState(""); + const [memoList, setMemoList] = useState([]); + + const handleMemoIdInputKeyDown = (event: React.KeyboardEvent) => { + if (event.key === "Enter") { + handleSaveBtnClick(); + } + }; + + const handleMemoIdChanged = (event: React.ChangeEvent) => { + const memoId = event.target.value; + setMemoId(memoId.trim()); + }; + + const handleSaveBtnClick = async () => { + const id = Number(memoId); + if (isNaN(id)) { + toast.error("Invalid memo id"); + return; + } + + try { + const { memo } = await memoServiceClient.getMemo({ + id, + }); + if (!memo) { + toast.error("Not found memo"); + return; + } + + setMemoId(""); + setMemoList(unionBy([memo, ...memoList], "id")); + } catch (error: any) { + console.error(error); + toast.error(error.response.data.message); + } + }; + + const handleDeleteMemoRelation = async (memo: Memo) => { + setMemoList(memoList.filter((m) => m !== memo)); + }; + + const handleCloseDialog = () => { + if (onCancel) { + onCancel(); + } + destroy(); + }; + + const handleConfirmBtnClick = async () => { + if (onConfirm) { + onConfirm(memoList.map((memo) => memo.id)); + } + destroy(); + }; + + return ( + <> +
+

{"Relations"}

+ +
+
+ } + /> + {memoList.length > 0 && ( + <> +
+ {memoList.map((memo) => ( +
handleDeleteMemoRelation(memo)} + > + #{memo.id} + {memo.content} +
+ ))} +
+ + )} +
+ + +
+
+ + ); +}; + +function showCreateMemoRelationDialog(props: Omit) { + generateDialog( + { + className: "create-memo-relation-dialog", + dialogName: "create-memo-relation-dialog", + }, + CreateMemoRelationDialog, + props + ); +} + +export default showCreateMemoRelationDialog; diff --git a/web/src/components/Dialog/BaseDialog.tsx b/web/src/components/Dialog/BaseDialog.tsx index 6ca7087f..2aa14eb7 100644 --- a/web/src/components/Dialog/BaseDialog.tsx +++ b/web/src/components/Dialog/BaseDialog.tsx @@ -1,4 +1,5 @@ import { CssVarsProvider } from "@mui/joy"; +import classNames from "classnames"; import { useEffect, useRef } from "react"; import { createRoot } from "react-dom/client"; import { Provider } from "react-redux"; @@ -11,6 +12,7 @@ import "@/less/base-dialog.less"; interface DialogConfig { dialogName: string; className?: string; + containerClassName?: string; clickSpaceDestroy?: boolean; } @@ -19,7 +21,7 @@ interface Props extends DialogConfig, DialogProps { } const BaseDialog: React.FC = (props: Props) => { - const { children, className, clickSpaceDestroy, dialogName, destroy } = props; + const { children, className, containerClassName, clickSpaceDestroy, dialogName, destroy } = props; const dialogStore = useDialogStore(); const dialogContainerRef = useRef(null); const dialogIndex = dialogStore.state.dialogStack.findIndex((item) => item === dialogName); @@ -55,8 +57,8 @@ const BaseDialog: React.FC = (props: Props) => { }; return ( -
-
e.stopPropagation()}> +
+
e.stopPropagation()}> {children}
diff --git a/web/src/components/Header.tsx b/web/src/components/Header.tsx index c71a4fe2..d6753f1d 100644 --- a/web/src/components/Header.tsx +++ b/web/src/components/Header.tsx @@ -83,12 +83,12 @@ const Header = () => { return (
layoutStore.setHeaderStatus(false)} diff --git a/web/src/components/Memo.tsx b/web/src/components/Memo.tsx index 12e66196..b799d7e4 100644 --- a/web/src/components/Memo.tsx +++ b/web/src/components/Memo.tsx @@ -25,6 +25,7 @@ import "@/less/memo.less"; interface Props { memo: Memo; showVisibility?: boolean; + showPinnedStyle?: boolean; lazyRendering?: boolean; } @@ -229,7 +230,7 @@ const Memo: React.FC = (props: Props) => { return ( <> -
+
@@ -290,6 +291,12 @@ const Memo: React.FC = (props: Props) => {
{creator && ( <> + + + #{memo.id} + + + @@ -298,7 +305,7 @@ const Memo: React.FC = (props: Props) => { - {memo.pinned && ( + {memo.pinned && props.showPinnedStyle && ( <> diff --git a/web/src/components/MemoEditor/MemoEditorDialog.tsx b/web/src/components/MemoEditor/MemoEditorDialog.tsx index 4225c91d..b9aa7dea 100644 --- a/web/src/components/MemoEditor/MemoEditorDialog.tsx +++ b/web/src/components/MemoEditor/MemoEditorDialog.tsx @@ -31,7 +31,13 @@ const MemoEditorDialog: React.FC = ({ memoId, relationList, destroy }: Pr
- +
); @@ -42,6 +48,7 @@ export default function showMemoEditorDialog(props: Pick { }); }; + const handleAddMemoRelationBtnClick = () => { + showCreateMemoRelationDialog({ + onConfirm: (memoIdList) => { + setState((prevState) => ({ + ...prevState, + relationList: uniqBy( + [ + ...memoIdList.map((id) => ({ memoId: memoId || UNKNOWN_ID, relatedMemoId: id, type: "REFERENCE" as MemoRelationType })), + ...state.relationList, + ].filter((relation) => relation.relatedMemoId !== (memoId || UNKNOWN_ID)), + "relatedMemoId" + ), + })); + }, + }); + }; + const handleSetResourceList = (resourceList: Resource[]) => { setState((prevState) => ({ ...prevState, @@ -406,7 +424,10 @@ const MemoEditor = (props: Props) => {
handleTagSelectorClick(tag)} /> +