feat: implement inline memo editor

pull/3712/head
Steven 9 months ago
parent 956f21838d
commit e2fd79200e

@ -15,6 +15,7 @@ interface Props {
memo: Memo; memo: Memo;
className?: string; className?: string;
hiddenActions?: ("edit" | "archive" | "delete" | "share" | "pin")[]; hiddenActions?: ("edit" | "archive" | "delete" | "share" | "pin")[];
onEdit?: () => void;
} }
const MemoActionMenu = (props: Props) => { const MemoActionMenu = (props: Props) => {
@ -50,6 +51,12 @@ const MemoActionMenu = (props: Props) => {
}; };
const handleEditMemoClick = () => { const handleEditMemoClick = () => {
if (props.onEdit) {
props.onEdit();
return;
}
// TODO: remove me later.
showMemoEditorDialog({ showMemoEditorDialog({
memoName: memo.name, memoName: memo.name,
cacheKey: `${memo.name}-${memo.updateTime}`, cacheKey: `${memo.name}-${memo.updateTime}`,

@ -38,6 +38,7 @@ export interface Props {
autoFocus?: boolean; autoFocus?: boolean;
memoPatchRef?: React.MutableRefObject<Partial<Memo>>; memoPatchRef?: React.MutableRefObject<Partial<Memo>>;
onConfirm?: (memoName: string) => void; onConfirm?: (memoName: string) => void;
onCancel?: () => void;
} }
interface State { interface State {
@ -439,7 +440,12 @@ const MemoEditor = (props: Props) => {
))} ))}
</Select> </Select>
</div> </div>
<div className="shrink-0 flex flex-row justify-end items-center"> <div className="shrink-0 flex flex-row justify-end items-center gap-2">
{props.onCancel && (
<Button className="!font-normal" color="neutral" variant="plain" loading={state.isRequesting} onClick={props.onCancel}>
{t("common.cancel")}
</Button>
)}
<Button <Button
className="!font-normal" className="!font-normal"
disabled={!allowSave} disabled={!allowSave}

@ -14,7 +14,7 @@ import { convertVisibilityToString } from "@/utils/memo";
import Icon from "./Icon"; import Icon from "./Icon";
import MemoActionMenu from "./MemoActionMenu"; import MemoActionMenu from "./MemoActionMenu";
import MemoContent from "./MemoContent"; import MemoContent from "./MemoContent";
import showMemoEditorDialog from "./MemoEditor/MemoEditorDialog"; import MemoEditor from "./MemoEditor";
import MemoReactionistView from "./MemoReactionListView"; import MemoReactionistView from "./MemoReactionListView";
import MemoRelationListView from "./MemoRelationListView"; import MemoRelationListView from "./MemoRelationListView";
import MemoResourceListView from "./MemoResourceListView"; import MemoResourceListView from "./MemoResourceListView";
@ -45,6 +45,7 @@ const MemoView: React.FC<Props> = (props: Props) => {
const workspaceMemoRelatedSetting = const workspaceMemoRelatedSetting =
workspaceSettingStore.getWorkspaceSettingByKey(WorkspaceSettingKey.MEMO_RELATED).memoRelatedSetting || workspaceSettingStore.getWorkspaceSettingByKey(WorkspaceSettingKey.MEMO_RELATED).memoRelatedSetting ||
WorkspaceMemoRelatedSetting.fromPartial({}); WorkspaceMemoRelatedSetting.fromPartial({});
const [showEditor, setShowEditor] = useState<boolean>(false);
const [creator, setCreator] = useState(userStore.getUserByName(memo.creator)); const [creator, setCreator] = useState(userStore.getUserByName(memo.creator));
const memoContainerRef = useRef<HTMLDivElement>(null); const memoContainerRef = useRef<HTMLDivElement>(null);
const referencedMemos = memo.relations.filter((relation) => relation.type === MemoRelation_Type.REFERENCE); const referencedMemos = memo.relations.filter((relation) => relation.type === MemoRelation_Type.REFERENCE);
@ -85,10 +86,7 @@ const MemoView: React.FC<Props> = (props: Props) => {
if (workspaceMemoRelatedSetting.enableDoubleClickEdit) { if (workspaceMemoRelatedSetting.enableDoubleClickEdit) {
e.preventDefault(); e.preventDefault();
showMemoEditorDialog({ setShowEditor(true);
memoName: memo.name,
cacheKey: `${memo.name}-${memo.updateTime}`,
});
} }
}, []); }, []);
@ -137,6 +135,7 @@ const MemoView: React.FC<Props> = (props: Props) => {
</div> </div>
)} )}
</div> </div>
{!showEditor && (
<div className="flex flex-row justify-end items-center select-none shrink-0 gap-2"> <div className="flex flex-row justify-end items-center select-none shrink-0 gap-2">
<div className="w-auto invisible group-hover:visible flex flex-row justify-between items-center gap-2"> <div className="w-auto invisible group-hover:visible flex flex-row justify-between items-center gap-2">
{props.showVisibility && memo.visibility !== Visibility.PRIVATE && ( {props.showVisibility && memo.visibility !== Visibility.PRIVATE && (
@ -166,9 +165,29 @@ const MemoView: React.FC<Props> = (props: Props) => {
<Icon.Bookmark className="w-4 h-auto text-amber-500" /> <Icon.Bookmark className="w-4 h-auto text-amber-500" />
</Tooltip> </Tooltip>
)} )}
{!readonly && <MemoActionMenu className="-ml-1" memo={memo} hiddenActions={props.showPinned ? [] : ["pin"]} />} {!readonly && (
<MemoActionMenu
className="-ml-1"
memo={memo}
hiddenActions={props.showPinned ? [] : ["pin"]}
onEdit={() => setShowEditor(true)}
/>
)}
</div> </div>
)}
</div> </div>
{showEditor ? (
<MemoEditor
autoFocus
className="border-none !p-0 -mb-2"
cacheKey={`inline-memo-editor-${memo.name}`}
memoName={memo.name}
onConfirm={() => setShowEditor(false)}
onCancel={() => setShowEditor(false)}
/>
) : (
<>
<MemoContent <MemoContent
key={`${memo.name}-${memo.updateTime}`} key={`${memo.name}-${memo.updateTime}`}
memoName={memo.name} memoName={memo.name}
@ -181,6 +200,8 @@ const MemoView: React.FC<Props> = (props: Props) => {
<MemoResourceListView resources={memo.resources} /> <MemoResourceListView resources={memo.resources} />
<MemoRelationListView memo={memo} relations={referencedMemos} /> <MemoRelationListView memo={memo} relations={referencedMemos} />
<MemoReactionistView memo={memo} reactions={memo.reactions} /> <MemoReactionistView memo={memo} reactions={memo.reactions} />
</>
)}
</div> </div>
); );
}; };

@ -6,7 +6,7 @@ import { toast } from "react-hot-toast";
import { Link, useParams } from "react-router-dom"; import { Link, useParams } from "react-router-dom";
import Icon from "@/components/Icon"; import Icon from "@/components/Icon";
import { MemoDetailSidebar, MemoDetailSidebarDrawer } from "@/components/MemoDetailSidebar"; import { MemoDetailSidebar, MemoDetailSidebarDrawer } from "@/components/MemoDetailSidebar";
import showMemoEditorDialog from "@/components/MemoEditor/MemoEditorDialog"; import MemoEditor from "@/components/MemoEditor";
import MemoView from "@/components/MemoView"; import MemoView from "@/components/MemoView";
import MobileHeader from "@/components/MobileHeader"; import MobileHeader from "@/components/MobileHeader";
import useCurrentUser from "@/hooks/useCurrentUser"; import useCurrentUser from "@/hooks/useCurrentUser";
@ -27,6 +27,7 @@ const MemoDetail = () => {
const uid = params.uid; const uid = params.uid;
const memo = memoStore.getMemoByUid(uid || ""); const memo = memoStore.getMemoByUid(uid || "");
const [parentMemo, setParentMemo] = useState<Memo | undefined>(undefined); const [parentMemo, setParentMemo] = useState<Memo | undefined>(undefined);
const [showCommentEditor, setShowCommentEditor] = useState(false);
const commentRelations = const commentRelations =
memo?.relations.filter((relation) => relation.relatedMemo === memo.name && relation.type === MemoRelation_Type.COMMENT) || []; memo?.relations.filter((relation) => relation.relatedMemo === memo.name && relation.type === MemoRelation_Type.COMMENT) || [];
const comments = commentRelations.map((relation) => memoStore.getMemoByName(relation.memo)).filter((memo) => memo) as any as Memo[]; const comments = commentRelations.map((relation) => memoStore.getMemoByName(relation.memo)).filter((memo) => memo) as any as Memo[];
@ -66,17 +67,13 @@ const MemoDetail = () => {
} }
const handleShowCommentEditor = () => { const handleShowCommentEditor = () => {
showMemoEditorDialog({ setShowCommentEditor(true);
placeholder: t("editor.add-your-comment-here"),
parentMemoName: memo.name,
onConfirm: handleCommentCreated,
cacheKey: `${memo.name}-${memo.updateTime}-comment`,
});
}; };
const handleCommentCreated = async (memoCommentName: string) => { const handleCommentCreated = async (memoCommentName: string) => {
await memoStore.getOrFetchMemoByName(memoCommentName); await memoStore.getOrFetchMemoByName(memoCommentName);
await memoStore.getOrFetchMemoByName(memo.name, { skipCache: true }); await memoStore.getOrFetchMemoByName(memo.name, { skipCache: true });
setShowCommentEditor(false);
}; };
return ( return (
@ -145,6 +142,18 @@ const MemoDetail = () => {
</> </>
)} )}
</div> </div>
{showCommentEditor && (
<div className="w-full">
<MemoEditor
cacheKey={`${memo.name}-${memo.updateTime}-comment`}
placeholder={t("editor.add-your-comment-here")}
parentMemoName={memo.name}
autoFocus
onConfirm={handleCommentCreated}
onCancel={() => setShowCommentEditor(false)}
/>
</div>
)}
</div> </div>
</div> </div>
{md && ( {md && (

Loading…
Cancel
Save