feat: support updating display time

pull/3399/head v0.22.0
Steven 11 months ago
parent b0aad6f694
commit c25c57ab61

@ -277,6 +277,17 @@ func (s *APIV1Service) UpdateMemo(ctx context.Context, request *v1pb.UpdateMemoR
} else if path == "created_ts" { } else if path == "created_ts" {
createdTs := request.Memo.CreateTime.AsTime().Unix() createdTs := request.Memo.CreateTime.AsTime().Unix()
update.CreatedTs = &createdTs update.CreatedTs = &createdTs
} else if path == "display_ts" {
displayTs := request.Memo.DisplayTime.AsTime().Unix()
memoRelatedSetting, err := s.Store.GetWorkspaceMemoRelatedSetting(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get workspace memo related setting")
}
if memoRelatedSetting.DisplayWithUpdateTime {
update.UpdatedTs = &displayTs
} else {
update.CreatedTs = &displayTs
}
} else if path == "pinned" { } else if path == "pinned" {
if _, err := s.Store.UpsertMemoOrganizer(ctx, &store.MemoOrganizer{ if _, err := s.Store.UpsertMemoOrganizer(ctx, &store.MemoOrganizer{
MemoID: id, MemoID: id,

@ -77,10 +77,8 @@ export function generateDialog<T extends DialogProps>(
const cbs: DialogCallback = { const cbs: DialogCallback = {
destroy: () => { destroy: () => {
document.body.style.removeProperty("overflow"); document.body.style.removeProperty("overflow");
setTimeout(() => {
dialog.unmount(); dialog.unmount();
tempDiv.remove(); tempDiv.remove();
});
}, },
}; };

@ -15,7 +15,7 @@ const ExploreSidebar = (props: Props) => {
)} )}
> >
<SearchBar /> <SearchBar />
<TagsSection hideTips={true} /> <TagsSection readonly={true} />
</aside> </aside>
); );
}; };

@ -14,7 +14,7 @@ import Icon from "../Icon";
import showRenameTagDialog from "../RenameTagDialog"; import showRenameTagDialog from "../RenameTagDialog";
interface Props { interface Props {
hideTips?: boolean; readonly?: boolean;
} }
const TagsSection = (props: Props) => { const TagsSection = (props: Props) => {
@ -62,7 +62,7 @@ const TagsSection = (props: Props) => {
<div className="flex flex-col justify-start items-start w-full mt-3 px-1 h-auto shrink-0 flex-nowrap hide-scrollbar"> <div className="flex flex-col justify-start items-start w-full mt-3 px-1 h-auto shrink-0 flex-nowrap hide-scrollbar">
<div className="group flex flex-row justify-start items-center w-full gap-1 mb-1"> <div className="group flex flex-row justify-start items-center w-full gap-1 mb-1">
<span className="text-sm leading-6 font-mono text-gray-400 select-none">{t("common.tags")}</span> <span className="text-sm leading-6 font-mono text-gray-400 select-none">{t("common.tags")}</span>
{!props.hideTips && ( {!props.readonly && (
<div className={clsx("group-hover:block", tagAmounts.length > 0 ? "hidden" : "")}> <div className={clsx("group-hover:block", tagAmounts.length > 0 ? "hidden" : "")}>
<Tooltip title={"Rebuild"} placement="top"> <Tooltip title={"Rebuild"} placement="top">
<Icon.RefreshCcw <Icon.RefreshCcw
@ -80,7 +80,7 @@ const TagsSection = (props: Props) => {
))} ))}
</div> </div>
) : ( ) : (
!props.hideTips && ( !props.readonly && (
<div className="p-2 border border-dashed dark:border-zinc-800 rounded-md flex flex-row justify-start items-start gap-1 text-gray-400 dark:text-gray-500"> <div className="p-2 border border-dashed dark:border-zinc-800 rounded-md flex flex-row justify-start items-start gap-1 text-gray-400 dark:text-gray-500">
<Icon.Tags /> <Icon.Tags />
<p className="mt-0.5 text-sm leading-snug italic">{t("tag.create-tags-guide")}</p> <p className="mt-0.5 text-sm leading-snug italic">{t("tag.create-tags-guide")}</p>

@ -3,7 +3,7 @@ interface Props {
} }
const Code: React.FC<Props> = ({ content }: Props) => { const Code: React.FC<Props> = ({ content }: Props) => {
return <code className="inline break-all px-1 font-mono rounded bg-gray-100 dark:bg-zinc-700">{content}</code>; return <code className="inline break-all px-1 font-mono text-sm rounded opacity-80 bg-gray-100 dark:bg-zinc-700">{content}</code>;
}; };
export default Code; export default Code;

@ -1,6 +1,8 @@
import { IconButton } from "@mui/joy"; import { IconButton } from "@mui/joy";
import { useEffect } from "react"; import clsx from "clsx";
import { useTagStore } from "@/store/v1"; import { useEffect, useRef, useState } from "react";
import { useMemoStore, useTagStore } from "@/store/v1";
import { Memo } from "@/types/proto/api/v1/memo_service";
import MemoEditor, { Props as MemoEditorProps } from "."; import MemoEditor, { Props as MemoEditorProps } from ".";
import { generateDialog } from "../Dialog"; import { generateDialog } from "../Dialog";
import Icon from "../Icon"; import Icon from "../Icon";
@ -8,7 +10,7 @@ import Icon from "../Icon";
interface Props extends DialogProps, MemoEditorProps {} interface Props extends DialogProps, MemoEditorProps {}
const MemoEditorDialog: React.FC<Props> = ({ const MemoEditorDialog: React.FC<Props> = ({
memoName: memo, memoName,
parentMemoName, parentMemoName,
placeholder, placeholder,
cacheKey, cacheKey,
@ -17,11 +19,21 @@ const MemoEditorDialog: React.FC<Props> = ({
destroy, destroy,
}: Props) => { }: Props) => {
const tagStore = useTagStore(); const tagStore = useTagStore();
const memoStore = useMemoStore();
const [displayTime, setDisplayTime] = useState<string | undefined>(memoStore.getMemoByName(memoName || "")?.displayTime?.toISOString());
const memoPatchRef = useRef<Partial<Memo>>({
displayTime: memoStore.getMemoByName(memoName || "")?.displayTime,
});
useEffect(() => { useEffect(() => {
tagStore.fetchTags(undefined, { skipCache: false }); tagStore.fetchTags(undefined, { skipCache: false });
}, []); }, []);
const updateDisplayTime = (displayTime: string) => {
setDisplayTime(displayTime);
memoPatchRef.current.displayTime = new Date(displayTime);
};
const handleCloseBtnClick = () => { const handleCloseBtnClick = () => {
destroy(); destroy();
}; };
@ -35,10 +47,25 @@ const MemoEditorDialog: React.FC<Props> = ({
return ( return (
<> <>
<div className="w-full flex flex-row justify-between items-center mb-2"> <div className="w-full flex flex-row justify-between items-center">
<div className="flex flex-row justify-start items-center"> <div className={clsx("flex flex-row justify-start items-center", !displayTime && "mb-2")}>
{displayTime ? (
<div className="relative">
<span className="cursor-pointer text-gray-500 dark:text-gray-400">{new Date(displayTime).toLocaleString()}</span>
<input
className="inset-0 absolute z-1 opacity-0"
type="datetime-local"
value={displayTime}
onFocus={(e: any) => e.target.showPicker()}
onChange={(e) => updateDisplayTime(e.target.value)}
/>
</div>
) : (
<>
<img className="w-6 h-auto rounded-full shadow" src={"/full-logo.webp"} alt="" /> <img className="w-6 h-auto rounded-full shadow" src={"/full-logo.webp"} alt="" />
<p className="ml-1 text-lg opacity-80 dark:text-gray-300">Memos</p> <p className="ml-1 text-lg opacity-80 dark:text-gray-300">Memos</p>
</>
)}
</div> </div>
<IconButton size="sm" onClick={handleCloseBtnClick}> <IconButton size="sm" onClick={handleCloseBtnClick}>
<Icon.X className="w-5 h-auto" /> <Icon.X className="w-5 h-auto" />
@ -47,11 +74,12 @@ const MemoEditorDialog: React.FC<Props> = ({
<div className="flex flex-col justify-start items-start max-w-full w-[36rem]"> <div className="flex flex-col justify-start items-start max-w-full w-[36rem]">
<MemoEditor <MemoEditor
className="border-none !p-0 -mb-2" className="border-none !p-0 -mb-2"
cacheKey={`memo-editor-${cacheKey || memo}`} cacheKey={`memo-editor-${cacheKey || memoName}`}
memoName={memo} memoName={memoName}
parentMemoName={parentMemoName} parentMemoName={parentMemoName}
placeholder={placeholder} placeholder={placeholder}
relationList={relationList} relationList={relationList}
memoPatchRef={memoPatchRef}
onConfirm={handleConfirm} onConfirm={handleConfirm}
autoFocus autoFocus
/> />

@ -9,7 +9,7 @@ import { isValidUrl } from "@/helpers/utils";
import useCurrentUser from "@/hooks/useCurrentUser"; import useCurrentUser from "@/hooks/useCurrentUser";
import { useMemoStore, useResourceStore, useUserStore, useWorkspaceSettingStore } from "@/store/v1"; import { useMemoStore, useResourceStore, useUserStore, useWorkspaceSettingStore } from "@/store/v1";
import { MemoRelation, MemoRelation_Type } from "@/types/proto/api/v1/memo_relation_service"; import { MemoRelation, MemoRelation_Type } from "@/types/proto/api/v1/memo_relation_service";
import { Visibility } from "@/types/proto/api/v1/memo_service"; import { Memo, Visibility } from "@/types/proto/api/v1/memo_service";
import { Resource } from "@/types/proto/api/v1/resource_service"; import { Resource } from "@/types/proto/api/v1/resource_service";
import { UserSetting } from "@/types/proto/api/v1/user_service"; import { UserSetting } from "@/types/proto/api/v1/user_service";
import { WorkspaceMemoRelatedSetting } from "@/types/proto/api/v1/workspace_setting_service"; import { WorkspaceMemoRelatedSetting } from "@/types/proto/api/v1/workspace_setting_service";
@ -36,8 +36,8 @@ export interface Props {
parentMemoName?: string; parentMemoName?: string;
relationList?: MemoRelation[]; relationList?: MemoRelation[];
autoFocus?: boolean; autoFocus?: boolean;
memoPatchRef?: React.MutableRefObject<Partial<Memo>>;
onConfirm?: (memoName: string) => void; onConfirm?: (memoName: string) => void;
onEditPrevious?: () => void;
} }
interface State { interface State {
@ -159,12 +159,6 @@ const MemoEditor = (props: Props) => {
} }
return; return;
} }
if (!!props.onEditPrevious && event.key === "ArrowDown" && !state.isComposing && editorRef.current.getContent() === "") {
event.preventDefault();
props.onEditPrevious();
return;
}
}; };
const handleMemoVisibilityChange = (visibility: Visibility) => { const handleMemoVisibilityChange = (visibility: Visibility) => {
@ -293,13 +287,18 @@ const MemoEditor = (props: Props) => {
if (memoName) { if (memoName) {
const prevMemo = await memoStore.getOrFetchMemoByName(memoName); const prevMemo = await memoStore.getOrFetchMemoByName(memoName);
if (prevMemo) { if (prevMemo) {
const updateMask = ["content", "visibility"];
if (props.memoPatchRef?.current?.displayTime) {
updateMask.push("display_ts");
}
const memo = await memoStore.updateMemo( const memo = await memoStore.updateMemo(
{ {
name: prevMemo.name, name: prevMemo.name,
content, content,
visibility: state.memoVisibility, visibility: state.memoVisibility,
...props.memoPatchRef?.current,
}, },
["content", "visibility"], updateMask,
); );
await memoServiceClient.setMemoResources({ await memoServiceClient.setMemoResources({
name: memo.name, name: memo.name,

@ -1,11 +1,10 @@
import { Button } from "@mui/joy"; import { Button } from "@mui/joy";
import clsx from "clsx"; import clsx from "clsx";
import { useCallback, useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Empty from "@/components/Empty"; import Empty from "@/components/Empty";
import { HomeSidebar, HomeSidebarDrawer } from "@/components/HomeSidebar"; import { HomeSidebar, HomeSidebarDrawer } from "@/components/HomeSidebar";
import Icon from "@/components/Icon"; import Icon from "@/components/Icon";
import MemoEditor from "@/components/MemoEditor"; import MemoEditor from "@/components/MemoEditor";
import showMemoEditorDialog from "@/components/MemoEditor/MemoEditorDialog";
import MemoFilter from "@/components/MemoFilter"; import MemoFilter from "@/components/MemoFilter";
import MemoView from "@/components/MemoView"; import MemoView from "@/components/MemoView";
import MobileHeader from "@/components/MobileHeader"; import MobileHeader from "@/components/MobileHeader";
@ -59,14 +58,6 @@ const Home = () => {
setNextPageToken(response.nextPageToken); setNextPageToken(response.nextPageToken);
}; };
const handleEditPrevious = useCallback(() => {
const lastMemo = memoList.value[memoList.value.length - 1];
showMemoEditorDialog({
memoName: lastMemo.name,
cacheKey: `${lastMemo.name}-${lastMemo.displayTime}`,
});
}, [memoList]);
return ( return (
<section className="@container w-full max-w-5xl min-h-full flex flex-col justify-start items-center sm:pt-3 md:pt-6 pb-8"> <section className="@container w-full max-w-5xl min-h-full flex flex-col justify-start items-center sm:pt-3 md:pt-6 pb-8">
{!md && ( {!md && (
@ -76,7 +67,7 @@ const Home = () => {
)} )}
<div className={clsx("w-full flex flex-row justify-start items-start px-4 sm:px-6 gap-4")}> <div className={clsx("w-full flex flex-row justify-start items-start px-4 sm:px-6 gap-4")}>
<div className={clsx(md ? "w-[calc(100%-15rem)]" : "w-full")}> <div className={clsx(md ? "w-[calc(100%-15rem)]" : "w-full")}>
<MemoEditor className="mb-2" cacheKey="home-memo-editor" onEditPrevious={handleEditPrevious} /> <MemoEditor className="mb-2" cacheKey="home-memo-editor" />
<div className="flex flex-col justify-start items-start w-full max-w-full"> <div className="flex flex-col justify-start items-start w-full max-w-full">
<MemoFilter className="px-2 pb-2" /> <MemoFilter className="px-2 pb-2" />
{sortedMemos.map((memo) => ( {sortedMemos.map((memo) => (

Loading…
Cancel
Save