mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
chore: migrate memo route
This commit is contained in:
@ -4,7 +4,7 @@ import { useEffect, useState } from "react";
|
|||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import { activityServiceClient } from "@/grpcweb";
|
import { activityServiceClient } from "@/grpcweb";
|
||||||
import useNavigateTo from "@/hooks/useNavigateTo";
|
import useNavigateTo from "@/hooks/useNavigateTo";
|
||||||
import { useInboxStore, extractUsernameFromName } from "@/store/v1";
|
import { useInboxStore, extractUsernameFromName, useMemoStore } from "@/store/v1";
|
||||||
import { Activity } from "@/types/proto/api/v2/activity_service";
|
import { Activity } from "@/types/proto/api/v2/activity_service";
|
||||||
import { Inbox, Inbox_Status } from "@/types/proto/api/v2/inbox_service";
|
import { Inbox, Inbox_Status } from "@/types/proto/api/v2/inbox_service";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
@ -18,6 +18,7 @@ const MemoCommentMessage = ({ inbox }: Props) => {
|
|||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const navigateTo = useNavigateTo();
|
const navigateTo = useNavigateTo();
|
||||||
const inboxStore = useInboxStore();
|
const inboxStore = useInboxStore();
|
||||||
|
const memoStore = useMemoStore();
|
||||||
const [activity, setActivity] = useState<Activity | undefined>(undefined);
|
const [activity, setActivity] = useState<Activity | undefined>(undefined);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -34,11 +35,19 @@ const MemoCommentMessage = ({ inbox }: Props) => {
|
|||||||
});
|
});
|
||||||
}, [inbox.activityId]);
|
}, [inbox.activityId]);
|
||||||
|
|
||||||
const handleNavigateToMemo = () => {
|
const handleNavigateToMemo = async () => {
|
||||||
if (!activity?.payload?.memoComment?.relatedMemoId) {
|
const relatedMemoId = activity?.payload?.memoComment?.relatedMemoId;
|
||||||
|
if (!relatedMemoId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
navigateTo(`/m/${activity?.payload?.memoComment?.relatedMemoId}`);
|
|
||||||
|
const memo = await memoStore.getOrFetchMemoById(relatedMemoId);
|
||||||
|
if (!memo) {
|
||||||
|
toast.error("Memo not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateTo(`/m/${memo.name}`);
|
||||||
if (inbox.status === Inbox_Status.UNREAD) {
|
if (inbox.status === Inbox_Status.UNREAD) {
|
||||||
handleArchiveMessage(true);
|
handleArchiveMessage(true);
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ const MemoRelationListView = (props: Props) => {
|
|||||||
<div key={memo.id} className="block w-auto max-w-[50%]">
|
<div key={memo.id} className="block w-auto max-w-[50%]">
|
||||||
<Link
|
<Link
|
||||||
className="px-2 border rounded-md w-auto text-sm leading-6 flex flex-row justify-start items-center flex-nowrap text-gray-600 dark:text-gray-400 dark:border-zinc-700 dark:bg-zinc-900 hover:shadow hover:opacity-80"
|
className="px-2 border rounded-md w-auto text-sm leading-6 flex flex-row justify-start items-center flex-nowrap text-gray-600 dark:text-gray-400 dark:border-zinc-700 dark:bg-zinc-900 hover:shadow hover:opacity-80"
|
||||||
to={`/m/${memo.id}`}
|
to={`/m/${memo.name}`}
|
||||||
unstable_viewTransition
|
unstable_viewTransition
|
||||||
>
|
>
|
||||||
<Tooltip title="Reference" placement="top">
|
<Tooltip title="Reference" placement="top">
|
||||||
@ -64,7 +64,7 @@ const MemoRelationListView = (props: Props) => {
|
|||||||
<div key={memo.id} className="block w-auto max-w-[50%]">
|
<div key={memo.id} className="block w-auto max-w-[50%]">
|
||||||
<Link
|
<Link
|
||||||
className="px-2 border rounded-md w-auto text-sm leading-6 flex flex-row justify-start items-center flex-nowrap text-gray-600 dark:text-gray-300 dark:border-gray-600 hover:shadow hover:opacity-80"
|
className="px-2 border rounded-md w-auto text-sm leading-6 flex flex-row justify-start items-center flex-nowrap text-gray-600 dark:text-gray-300 dark:border-gray-600 hover:shadow hover:opacity-80"
|
||||||
to={`/m/${memo.id}`}
|
to={`/m/${memo.name}`}
|
||||||
unstable_viewTransition
|
unstable_viewTransition
|
||||||
>
|
>
|
||||||
<Tooltip title="Backlink" placement="top">
|
<Tooltip title="Backlink" placement="top">
|
||||||
|
@ -75,7 +75,7 @@ const MemoView: React.FC<Props> = (props: Props) => {
|
|||||||
if (event.altKey) {
|
if (event.altKey) {
|
||||||
showChangeMemoCreatedTsDialog(memo.id);
|
showChangeMemoCreatedTsDialog(memo.id);
|
||||||
} else {
|
} else {
|
||||||
navigateTo(`/m/${memo.id}`);
|
navigateTo(`/m/${memo.name}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleCopyLinkBtnClick = () => {
|
const handleCopyLinkBtnClick = () => {
|
||||||
copy(`${window.location.origin}/m/${memo.id}`);
|
copy(`${window.location.origin}/m/${memo.name}`);
|
||||||
toast.success(t("message.succeed-copy-link"));
|
toast.success(t("message.succeed-copy-link"));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -33,8 +33,8 @@ const MemoDetail = () => {
|
|||||||
const memoStore = useMemoStore();
|
const memoStore = useMemoStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const [creator, setCreator] = useState<User>();
|
const [creator, setCreator] = useState<User>();
|
||||||
const memoId = Number(params.memoId);
|
const memoName = params.memoName;
|
||||||
const memo = memoStore.getMemoById(memoId);
|
const memo = memoStore.getMemoByName(memoName || "");
|
||||||
const [parentMemo, setParentMemo] = useState<Memo | undefined>(undefined);
|
const [parentMemo, setParentMemo] = useState<Memo | undefined>(undefined);
|
||||||
const referenceRelations = memo?.relations.filter((relation) => relation.type === MemoRelation_Type.REFERENCE) || [];
|
const referenceRelations = memo?.relations.filter((relation) => relation.type === MemoRelation_Type.REFERENCE) || [];
|
||||||
const commentRelations =
|
const commentRelations =
|
||||||
@ -44,9 +44,9 @@ const MemoDetail = () => {
|
|||||||
|
|
||||||
// Prepare memo.
|
// Prepare memo.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (memoId && !isNaN(memoId)) {
|
if (memoName) {
|
||||||
memoStore
|
memoStore
|
||||||
.getOrFetchMemoById(memoId)
|
.getOrFetchMemoByName(memoName)
|
||||||
.then(async (memo) => {
|
.then(async (memo) => {
|
||||||
const user = await userStore.getOrFetchUserByUsername(extractUsernameFromName(memo.creator));
|
const user = await userStore.getOrFetchUserByUsername(extractUsernameFromName(memo.creator));
|
||||||
setCreator(user);
|
setCreator(user);
|
||||||
@ -58,7 +58,7 @@ const MemoDetail = () => {
|
|||||||
} else {
|
} else {
|
||||||
navigateTo("/404");
|
navigateTo("/404");
|
||||||
}
|
}
|
||||||
}, [memoId]);
|
}, [memoName]);
|
||||||
|
|
||||||
// Prepare memo comments.
|
// Prepare memo comments.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -100,7 +100,7 @@ const MemoDetail = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleCopyLinkBtnClick = () => {
|
const handleCopyLinkBtnClick = () => {
|
||||||
copy(`${window.location.origin}/m/${memo.id}`);
|
copy(`${window.location.origin}/m/${memo.name}`);
|
||||||
toast.success(t("message.succeed-copy-link"));
|
toast.success(t("message.succeed-copy-link"));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ const MemoDetail = () => {
|
|||||||
<div className="w-auto mb-2">
|
<div className="w-auto mb-2">
|
||||||
<Link
|
<Link
|
||||||
className="px-3 py-1 border rounded-lg max-w-xs w-auto text-sm flex flex-row justify-start items-center flex-nowrap text-gray-600 dark:text-gray-400 dark:border-gray-500 hover:shadow hover:opacity-80"
|
className="px-3 py-1 border rounded-lg max-w-xs w-auto text-sm flex flex-row justify-start items-center flex-nowrap text-gray-600 dark:text-gray-400 dark:border-gray-500 hover:shadow hover:opacity-80"
|
||||||
to={`/m/${parentMemo.id}`}
|
to={`/m/${parentMemo.name}`}
|
||||||
unstable_viewTransition
|
unstable_viewTransition
|
||||||
>
|
>
|
||||||
<Icon.ArrowUpLeftFromCircle className="w-4 h-auto shrink-0 opacity-60 mr-2" />
|
<Icon.ArrowUpLeftFromCircle className="w-4 h-auto shrink-0 opacity-60 mr-2" />
|
||||||
|
@ -10,6 +10,7 @@ import ResourceIcon from "@/components/ResourceIcon";
|
|||||||
import { resourceServiceClient } from "@/grpcweb";
|
import { resourceServiceClient } from "@/grpcweb";
|
||||||
import useLoading from "@/hooks/useLoading";
|
import useLoading from "@/hooks/useLoading";
|
||||||
import i18n from "@/i18n";
|
import i18n from "@/i18n";
|
||||||
|
import { useMemoStore } from "@/store/v1";
|
||||||
import { Resource } from "@/types/proto/api/v2/resource_service";
|
import { Resource } from "@/types/proto/api/v2/resource_service";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
|
|
||||||
@ -38,6 +39,7 @@ const Resources = () => {
|
|||||||
const [state, setState] = useState<State>({
|
const [state, setState] = useState<State>({
|
||||||
searchQuery: "",
|
searchQuery: "",
|
||||||
});
|
});
|
||||||
|
const memoStore = useMemoStore();
|
||||||
const [resources, setResources] = useState<Resource[]>([]);
|
const [resources, setResources] = useState<Resource[]>([]);
|
||||||
const filteredResources = resources.filter((resource) => includes(resource.filename, state.searchQuery));
|
const filteredResources = resources.filter((resource) => includes(resource.filename, state.searchQuery));
|
||||||
const groupedResources = groupResourcesByDate(filteredResources.filter((resoure) => resoure.memoId));
|
const groupedResources = groupResourcesByDate(filteredResources.filter((resoure) => resoure.memoId));
|
||||||
@ -47,6 +49,7 @@ const Resources = () => {
|
|||||||
resourceServiceClient.listResources({}).then(({ resources }) => {
|
resourceServiceClient.listResources({}).then(({ resources }) => {
|
||||||
setResources(resources);
|
setResources(resources);
|
||||||
loadingState.setFinish();
|
loadingState.setFinish();
|
||||||
|
Promise.all(resources.map((resource) => (resource.memoId ? memoStore.getOrFetchMemoById(resource.memoId) : null)));
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -109,6 +112,7 @@ const Resources = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="w-full max-w-[calc(100%-4rem)] sm:max-w-[calc(100%-6rem)] flex flex-row justify-start items-start gap-4 flex-wrap">
|
<div className="w-full max-w-[calc(100%-4rem)] sm:max-w-[calc(100%-6rem)] flex flex-row justify-start items-start gap-4 flex-wrap">
|
||||||
{resources.map((resource) => {
|
{resources.map((resource) => {
|
||||||
|
const relatedMemo = resource.memoId ? memoStore.getMemoById(resource.memoId) : null;
|
||||||
return (
|
return (
|
||||||
<div key={resource.id} className="w-24 sm:w-32 h-auto flex flex-col justify-start items-start">
|
<div key={resource.id} className="w-24 sm:w-32 h-auto flex flex-col justify-start items-start">
|
||||||
<div className="w-24 h-24 flex justify-center items-center sm:w-32 sm:h-32 border dark:border-zinc-900 overflow-clip rounded-xl cursor-pointer hover:shadow hover:opacity-80">
|
<div className="w-24 h-24 flex justify-center items-center sm:w-32 sm:h-32 border dark:border-zinc-900 overflow-clip rounded-xl cursor-pointer hover:shadow hover:opacity-80">
|
||||||
@ -116,13 +120,15 @@ const Resources = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="w-full max-w-full flex flex-row justify-between items-center mt-1 px-1">
|
<div className="w-full max-w-full flex flex-row justify-between items-center mt-1 px-1">
|
||||||
<p className="text-xs shrink text-gray-400 truncate">{resource.filename}</p>
|
<p className="text-xs shrink text-gray-400 truncate">{resource.filename}</p>
|
||||||
<Link
|
{relatedMemo && (
|
||||||
className="shrink-0 text-xs ml-1 text-gray-400 hover:underline hover:text-blue-600"
|
<Link
|
||||||
to={`/m/${resource.memoId}`}
|
className="shrink-0 text-xs ml-1 text-gray-400 hover:underline hover:text-blue-600"
|
||||||
target="_blank"
|
to={`/m/${relatedMemo.name}`}
|
||||||
>
|
target="_blank"
|
||||||
#{resource.memoId}
|
>
|
||||||
</Link>
|
#{relatedMemo.id}
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -112,7 +112,7 @@ const router = createBrowserRouter([
|
|||||||
element: <Explore />,
|
element: <Explore />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "m/:memoId",
|
path: "m/:memoName",
|
||||||
element: <MemoDetail />,
|
element: <MemoDetail />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -47,6 +47,28 @@ export const useMemoStore = create(
|
|||||||
getMemoById: (id: number) => {
|
getMemoById: (id: number) => {
|
||||||
return get().memoMapById[id];
|
return get().memoMapById[id];
|
||||||
},
|
},
|
||||||
|
getOrFetchMemoByName: async (name: string) => {
|
||||||
|
const memoMap = get().memoMapById;
|
||||||
|
const memo = Object.values(memoMap).find((memo) => memo.name === name);
|
||||||
|
if (memo) {
|
||||||
|
return memo;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await memoServiceClient.getMemoByName({
|
||||||
|
name,
|
||||||
|
});
|
||||||
|
if (!res.memo) {
|
||||||
|
throw new Error("Memo not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
memoMap[res.memo.id] = res.memo;
|
||||||
|
set({ memoMapById: memoMap });
|
||||||
|
return res.memo;
|
||||||
|
},
|
||||||
|
getMemoByName: (name: string) => {
|
||||||
|
const memoMap = get().memoMapById;
|
||||||
|
return Object.values(memoMap).find((memo) => memo.name === name);
|
||||||
|
},
|
||||||
createMemo: async (request: CreateMemoRequest) => {
|
createMemo: async (request: CreateMemoRequest) => {
|
||||||
const { memo } = await memoServiceClient.createMemo(request);
|
const { memo } = await memoServiceClient.createMemo(request);
|
||||||
if (!memo) {
|
if (!memo) {
|
||||||
|
Reference in New Issue
Block a user