Commit 5673e29e authored by Steven's avatar Steven

chore: compose memo in backend

parent feefaabc
......@@ -429,6 +429,16 @@ func (s *APIV2Service) convertMemoFromStore(ctx context.Context, memo *store.Mem
return nil, errors.Wrap(err, "failed to get creator")
}
listMemoRelationsResponse, err := s.ListMemoRelations(ctx, &apiv2pb.ListMemoRelationsRequest{Id: memo.ID})
if err != nil {
return nil, errors.Wrap(err, "failed to list memo relations")
}
listMemoResourcesResponse, err := s.ListMemoResources(ctx, &apiv2pb.ListMemoResourcesRequest{Id: memo.ID})
if err != nil {
return nil, errors.Wrap(err, "failed to list memo resources")
}
return &apiv2pb.Memo{
Id: int32(memo.ID),
RowStatus: convertRowStatusFromStore(memo.RowStatus),
......@@ -441,6 +451,8 @@ func (s *APIV2Service) convertMemoFromStore(ctx context.Context, memo *store.Mem
Nodes: convertFromASTNodes(rawNodes),
Visibility: convertVisibilityFromStore(memo.Visibility),
Pinned: memo.Pinned,
Relations: listMemoRelationsResponse.Relations,
Resources: listMemoResourcesResponse.Resources,
}, nil
}
......
......@@ -8,6 +8,7 @@ import "api/v2/memo_relation_service.proto";
import "api/v2/resource_service.proto";
import "google/api/annotations.proto";
import "google/api/client.proto";
import "google/api/field_behavior.proto";
import "google/protobuf/field_mask.proto";
import "google/protobuf/timestamp.proto";
......@@ -115,6 +116,10 @@ message Memo {
Visibility visibility = 10;
bool pinned = 11;
repeated Resource resources = 12 [(google.api.field_behavior) = OUTPUT_ONLY];
repeated MemoRelation relations = 13 [(google.api.field_behavior) = OUTPUT_ONLY];
}
message CreateMemoRequest {
......
......@@ -1803,6 +1803,8 @@
| nodes | [Node](#memos-api-v2-Node) | repeated | |
| visibility | [Visibility](#memos-api-v2-Visibility) | | |
| pinned | [bool](#bool) | | |
| resources | [Resource](#memos-api-v2-Resource) | repeated | |
| relations | [MemoRelation](#memos-api-v2-MemoRelation) | repeated | |
......
This diff is collapsed.
......@@ -10,9 +10,8 @@ import useNavigateTo from "@/hooks/useNavigateTo";
import { useFilterStore } from "@/store/module";
import { useUserV1Store, extractUsernameFromName, useMemoV1Store } from "@/store/v1";
import { RowStatus } from "@/types/proto/api/v2/common";
import { MemoRelation, MemoRelation_Type } from "@/types/proto/api/v2/memo_relation_service";
import { MemoRelation_Type } from "@/types/proto/api/v2/memo_relation_service";
import { Memo, Visibility } from "@/types/proto/api/v2/memo_service";
import { Resource } from "@/types/proto/api/v2/resource_service";
import { useTranslate } from "@/utils/i18n";
import { convertVisibilityToString } from "@/utils/memo";
import showChangeMemoCreatedTsDialog from "./ChangeMemoCreatedTsDialog";
......@@ -50,10 +49,8 @@ const MemoView: React.FC<Props> = (props: Props) => {
const [displayTime, setDisplayTime] = useState<string>(getRelativeTimeString(getTimeStampByDate(memo.displayTime)));
const [creator, setCreator] = useState(userV1Store.getUserByUsername(extractUsernameFromName(memo.creator)));
const [parentMemo, setParentMemo] = useState<Memo | undefined>(undefined);
const [resources, setResources] = useState<Resource[]>([]);
const [memoRelations, setMemoRelations] = useState<MemoRelation[]>([]);
const memoContainerRef = useRef<HTMLDivElement>(null);
const referenceRelations = memoRelations.filter((relation) => relation.type === MemoRelation_Type.REFERENCE);
const referenceRelations = memo.relations.filter((relation) => relation.type === MemoRelation_Type.REFERENCE);
const readonly = memo.creator !== user?.name;
// Prepare memo creator.
......@@ -101,20 +98,14 @@ const MemoView: React.FC<Props> = (props: Props) => {
return;
}
memoStore.fetchMemoResources(memo.id).then((resources: Resource[]) => {
setResources(resources);
});
memoStore.fetchMemoRelations(memo.id).then((relations: MemoRelation[]) => {
setMemoRelations(relations);
const parentMemoId = relations.find(
(relation) => relation.memoId === memo.id && relation.type === MemoRelation_Type.COMMENT
)?.relatedMemoId;
if (parentMemoId) {
memoStore.getOrFetchMemoById(parentMemoId).then((memo: Memo) => {
setParentMemo(memo);
});
}
});
const parentMemoId = memo.relations.find(
(relation) => relation.memoId === memo.id && relation.type === MemoRelation_Type.COMMENT
)?.relatedMemoId;
if (parentMemoId) {
memoStore.getOrFetchMemoById(parentMemoId).then((memo: Memo) => {
setParentMemo(memo);
});
}
}, [shouldRender]);
if (!shouldRender) {
......@@ -326,7 +317,7 @@ const MemoView: React.FC<Props> = (props: Props) => {
</div>
)}
<MemoContent content={memo.content} nodes={memo.nodes} onMemoContentClick={handleMemoContentClick} />
<MemoResourceListView resourceList={resources} />
<MemoResourceListView resourceList={memo.resources} />
<MemoRelationListView memo={memo} relationList={referenceRelations} />
</div>
);
......
import { Button } from "@mui/joy";
import copy from "copy-to-clipboard";
import React, { useEffect, useRef, useState } from "react";
import React, { useEffect, useRef } from "react";
import { toast } from "react-hot-toast";
import { getDateTimeString, getTimeString } from "@/helpers/datetime";
import useLoading from "@/hooks/useLoading";
import toImage from "@/labs/html2image";
import { useUserV1Store, extractUsernameFromName, useMemoV1Store } from "@/store/v1";
import { useUserV1Store, extractUsernameFromName } from "@/store/v1";
import { Memo } from "@/types/proto/api/v2/memo_service";
import { Resource } from "@/types/proto/api/v2/resource_service";
import { useTranslate } from "@/utils/i18n";
import { generateDialog } from "./Dialog";
import Icon from "./Icon";
......@@ -24,16 +23,13 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
const { memo, destroy } = props;
const t = useTranslate();
const userV1Store = useUserV1Store();
const memoStore = useMemoV1Store();
const downloadingImageState = useLoading(false);
const loadingState = useLoading();
const memoElRef = useRef<HTMLDivElement>(null);
const [resources, setResources] = useState<Resource[]>([]);
const user = userV1Store.getUserByUsername(extractUsernameFromName(memo.creator));
useEffect(() => {
(async () => {
setResources(await memoStore.fetchMemoResources(memo.id));
await userV1Store.getOrFetchUserByUsername(extractUsernameFromName(memo.creator));
loadingState.setFinish();
})();
......@@ -105,7 +101,7 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
<span className="w-full px-6 pt-5 pb-2 text-sm text-gray-500">{getTimeString(memo.displayTime)}</span>
<div className="w-full px-6 text-base pb-4">
<MemoContent content={memo.content} />
<MemoResourceListView resourceList={resources} />
<MemoResourceListView resourceList={memo.resources} />
</div>
<div className="flex flex-row justify-between items-center w-full bg-gray-100 dark:bg-zinc-700 py-4 px-6">
<div className="flex flex-row justify-start items-center">
......
import { useEffect, useState } from "react";
import Icon from "@/components/Icon";
import MemoContent from "@/components/MemoContent";
import MemoResourceListView from "@/components/MemoResourceListView";
import { getTimeString } from "@/helpers/datetime";
import { useMemoV1Store } from "@/store/v1";
import { MemoRelation, MemoRelation_Type } from "@/types/proto/api/v2/memo_relation_service";
import { MemoRelation_Type } from "@/types/proto/api/v2/memo_relation_service";
import { Memo } from "@/types/proto/api/v2/memo_service";
import { Resource } from "@/types/proto/api/v2/resource_service";
import MemoRelationListView from "./MemoRelationListView";
interface Props {
......@@ -15,18 +12,7 @@ interface Props {
const TimelineMemo = (props: Props) => {
const { memo } = props;
const memoStore = useMemoV1Store();
const [resources, setResources] = useState<Resource[]>([]);
const [relations, setRelations] = useState<MemoRelation[]>([]);
useEffect(() => {
memoStore.fetchMemoResources(memo.id).then((resources: Resource[]) => {
setResources(resources);
});
memoStore.fetchMemoRelations(memo.id).then((relations: MemoRelation[]) => {
setRelations(relations.filter((relation) => relation.type === MemoRelation_Type.REFERENCE));
});
}, [memo.id]);
const relations = memo.relations.filter((relation) => relation.type === MemoRelation_Type.REFERENCE);
return (
<div className="relative w-full flex flex-col justify-start items-start">
......@@ -36,7 +22,7 @@ const TimelineMemo = (props: Props) => {
<span className="opacity-60">#{memo.id}</span>
</div>
<MemoContent content={memo.content} nodes={memo.nodes} />
<MemoResourceListView resourceList={resources} />
<MemoResourceListView resourceList={memo.resources} />
<MemoRelationListView memo={memo} relationList={relations} />
</div>
);
......
......@@ -19,9 +19,8 @@ import { getDateTimeString } from "@/helpers/datetime";
import useCurrentUser from "@/hooks/useCurrentUser";
import useNavigateTo from "@/hooks/useNavigateTo";
import { useUserV1Store, useMemoV1Store, extractUsernameFromName } from "@/store/v1";
import { MemoRelation, MemoRelation_Type } from "@/types/proto/api/v2/memo_relation_service";
import { MemoRelation_Type } from "@/types/proto/api/v2/memo_relation_service";
import { Memo, Visibility } from "@/types/proto/api/v2/memo_service";
import { Resource } from "@/types/proto/api/v2/resource_service";
import { User } from "@/types/proto/api/v2/user_service";
import { useTranslate } from "@/utils/i18n";
import { convertVisibilityToString } from "@/utils/memo";
......@@ -38,12 +37,9 @@ const MemoDetail = () => {
const memo = memoStore.getMemoById(memoId);
const allowEdit = memo?.creatorId === currentUser.id;
const [parentMemo, setParentMemo] = useState<Memo | undefined>(undefined);
const [resources, setResources] = useState<Resource[]>([]);
const [memoRelations, setMemoRelations] = useState<MemoRelation[]>([]);
const referenceRelations = memoRelations.filter((relation) => relation.type === MemoRelation_Type.REFERENCE);
const commentRelations = memoRelations.filter(
(relation) => relation.relatedMemoId === memo?.id && relation.type === MemoRelation_Type.COMMENT
);
const referenceRelations = memo?.relations.filter((relation) => relation.type === MemoRelation_Type.REFERENCE) || [];
const commentRelations =
memo?.relations.filter((relation) => relation.relatedMemoId === memo?.id && relation.type === MemoRelation_Type.COMMENT) || [];
const comments = commentRelations.map((relation) => memoStore.getMemoById(relation.memoId)).filter((memo) => memo) as any as Memo[];
// Prepare memo.
......@@ -71,13 +67,7 @@ const MemoDetail = () => {
}
(async () => {
const resources = await memoStore.fetchMemoResources(memo.id);
setResources(resources);
const memoRelations = await memoStore.fetchMemoRelations(memo.id);
const commentRelations = memoRelations.filter(
(relation) => relation.relatedMemoId === memo.id && relation.type === MemoRelation_Type.COMMENT
);
const parentMemoId = memoRelations.find(
const parentMemoId = memo.relations.find(
(relation) => relation.memoId === memo.id && relation.type === MemoRelation_Type.COMMENT
)?.relatedMemoId;
if (parentMemoId) {
......@@ -85,9 +75,7 @@ const MemoDetail = () => {
setParentMemo(memo);
});
}
const requests = commentRelations.map((relation) => memoStore.getOrFetchMemoById(relation.memoId));
await Promise.all(requests);
setMemoRelations(memoRelations);
await Promise.all(commentRelations.map((relation) => memoStore.getOrFetchMemoById(relation.memoId)));
})();
}, [memo]);
......@@ -118,7 +106,7 @@ const MemoDetail = () => {
const handleCommentCreated = async (commentId: number) => {
await memoStore.getOrFetchMemoById(commentId);
setMemoRelations(await memoStore.fetchMemoRelations(memo.id));
await memoStore.getOrFetchMemoById(memo.id, true /* skip cache */);
};
return (
......@@ -142,7 +130,7 @@ const MemoDetail = () => {
<span className="text-gray-400 select-none">{getDateTimeString(memo.displayTime)}</span>
</div>
<MemoContent content={memo.content} />
<MemoResourceListView resourceList={resources} />
<MemoResourceListView resourceList={memo.resources} />
<MemoRelationListView memo={memo} relationList={referenceRelations} />
<div className="w-full mt-4 flex flex-col sm:flex-row justify-start sm:justify-between sm:items-center gap-2">
<div className="flex flex-row justify-start items-center">
......
......@@ -22,9 +22,9 @@ export const useMemoV1Store = create(
});
return memos;
},
getOrFetchMemoById: async (id: number) => {
getOrFetchMemoById: async (id: number, skipCache = false) => {
const memo = get().memoById.get(id);
if (memo) {
if (memo && !skipCache) {
return memo;
}
......@@ -82,18 +82,6 @@ export const useMemoV1Store = create(
return cloneDeep(state);
});
},
fetchMemoResources: async (id: number) => {
const { resources } = await memoServiceClient.listMemoResources({
id,
});
return resources;
},
fetchMemoRelations: async (id: number) => {
const { relations } = await memoServiceClient.listMemoRelations({
id,
});
return relations;
},
}))
);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment