From a297cc3140a535f6ab721fd2c0ddb7eb7de64ff3 Mon Sep 17 00:00:00 2001 From: Steven Date: Sat, 6 Jan 2024 09:48:11 +0800 Subject: [PATCH] chore: exclude comments in memo list response --- api/v1/memo.go | 24 -------------------- api/v2/memo_service.go | 13 ++++++++--- proto/api/v2/memo_service.proto | 6 +++-- proto/gen/api/v2/README.md | 1 + proto/gen/api/v2/memo_service.pb.go | 34 +++++++++++++++++++---------- store/db/mysql/memo.go | 9 +++++--- store/db/postgres/memo.go | 6 +++-- store/db/sqlite/memo.go | 6 +++-- store/memo.go | 11 +++++----- web/src/pages/MemoDetail.tsx | 15 +++++-------- 10 files changed, 64 insertions(+), 61 deletions(-) diff --git a/api/v1/memo.go b/api/v1/memo.go index d741d36bf..28ddc9f64 100644 --- a/api/v1/memo.go +++ b/api/v1/memo.go @@ -60,7 +60,6 @@ type Memo struct { Pinned bool `json:"pinned"` // Related fields - Parent *Memo `json:"parent"` CreatorName string `json:"creatorName"` CreatorUsername string `json:"creatorUsername"` ResourceList []*Resource `json:"resourceList"` @@ -184,11 +183,6 @@ func (s *APIV1Service) GetMemoList(c echo.Context) error { if rowStatus != "" { find.RowStatus = &rowStatus } - pinnedStr := c.QueryParam("pinned") - if pinnedStr != "" { - pinned := pinnedStr == "true" - find.Pinned = &pinned - } contentSearch := []string{} tag := c.QueryParam("tag") @@ -867,24 +861,6 @@ func (s *APIV1Service) convertMemoFromStore(ctx context.Context, memo *store.Mem relationList = append(relationList, convertMemoRelationFromStore(relation)) } memoMessage.RelationList = relationList - for _, relation := range memoMessage.RelationList { - if relation.MemoID == memoMessage.ID && relation.Type == MemoRelationComment { - parentMemo, err := s.Store.GetMemo(ctx, &store.FindMemo{ - ID: &relation.RelatedMemoID, - }) - if err != nil { - return nil, err - } - if parentMemo != nil { - parent, err := s.convertMemoFromStore(ctx, parentMemo) - if err != nil { - return nil, err - } - memoMessage.Parent = parent - } - } - } - return memoMessage, nil } diff --git a/api/v2/memo_service.go b/api/v2/memo_service.go index 0148f058a..e1d72c6aa 100644 --- a/api/v2/memo_service.go +++ b/api/v2/memo_service.go @@ -80,7 +80,10 @@ func (s *APIV2Service) CreateMemo(ctx context.Context, request *apiv2pb.CreateMe } func (s *APIV2Service) ListMemos(ctx context.Context, request *apiv2pb.ListMemosRequest) (*apiv2pb.ListMemosResponse, error) { - memoFind := &store.FindMemo{} + memoFind := &store.FindMemo{ + // Exclude comments by default. + ExcludeComments: true, + } if request.Filter != "" { filter, err := parseListMemosFilter(request.Filter) if err != nil { @@ -411,10 +414,13 @@ func (s *APIV2Service) GetUserMemosStats(ctx context.Context, request *apiv2pb.G if user == nil { return nil, status.Errorf(codes.NotFound, "user not found") } + normalRowStatus := store.Normal memos, err := s.Store.ListMemos(ctx, &store.FindMemo{ - CreatorID: &user.ID, - RowStatus: &normalRowStatus, + CreatorID: &user.ID, + RowStatus: &normalRowStatus, + ExcludeComments: true, + ExcludeContent: true, }) if err != nil { return nil, status.Errorf(codes.Internal, "failed to list memos") @@ -468,6 +474,7 @@ func (s *APIV2Service) convertMemoFromStore(ctx context.Context, memo *store.Mem Nodes: convertFromASTNodes(rawNodes), Visibility: convertVisibilityFromStore(memo.Visibility), Pinned: memo.Pinned, + ParentId: memo.ParentID, Relations: listMemoRelationsResponse.Relations, Resources: listMemoResourcesResponse.Resources, }, nil diff --git a/proto/api/v2/memo_service.proto b/proto/api/v2/memo_service.proto index f10ca50de..b633c0fc7 100644 --- a/proto/api/v2/memo_service.proto +++ b/proto/api/v2/memo_service.proto @@ -122,9 +122,11 @@ message Memo { bool pinned = 11; - repeated Resource resources = 12 [(google.api.field_behavior) = OUTPUT_ONLY]; + optional int32 parent_id = 12; - repeated MemoRelation relations = 13 [(google.api.field_behavior) = OUTPUT_ONLY]; + repeated Resource resources = 13 [(google.api.field_behavior) = OUTPUT_ONLY]; + + repeated MemoRelation relations = 14 [(google.api.field_behavior) = OUTPUT_ONLY]; } message CreateMemoRequest { diff --git a/proto/gen/api/v2/README.md b/proto/gen/api/v2/README.md index 99a2c2b1f..798434185 100644 --- a/proto/gen/api/v2/README.md +++ b/proto/gen/api/v2/README.md @@ -1935,6 +1935,7 @@ | nodes | [Node](#memos-api-v2-Node) | repeated | | | visibility | [Visibility](#memos-api-v2-Visibility) | | | | pinned | [bool](#bool) | | | +| parent_id | [int32](#int32) | optional | | | resources | [Resource](#memos-api-v2-Resource) | repeated | | | relations | [MemoRelation](#memos-api-v2-MemoRelation) | repeated | | diff --git a/proto/gen/api/v2/memo_service.pb.go b/proto/gen/api/v2/memo_service.pb.go index ddeb3111f..9cc32a028 100644 --- a/proto/gen/api/v2/memo_service.pb.go +++ b/proto/gen/api/v2/memo_service.pb.go @@ -93,8 +93,9 @@ type Memo struct { Nodes []*Node `protobuf:"bytes,9,rep,name=nodes,proto3" json:"nodes,omitempty"` Visibility Visibility `protobuf:"varint,10,opt,name=visibility,proto3,enum=memos.api.v2.Visibility" json:"visibility,omitempty"` Pinned bool `protobuf:"varint,11,opt,name=pinned,proto3" json:"pinned,omitempty"` - Resources []*Resource `protobuf:"bytes,12,rep,name=resources,proto3" json:"resources,omitempty"` - Relations []*MemoRelation `protobuf:"bytes,13,rep,name=relations,proto3" json:"relations,omitempty"` + ParentId *int32 `protobuf:"varint,12,opt,name=parent_id,json=parentId,proto3,oneof" json:"parent_id,omitempty"` + Resources []*Resource `protobuf:"bytes,13,rep,name=resources,proto3" json:"resources,omitempty"` + Relations []*MemoRelation `protobuf:"bytes,14,rep,name=relations,proto3" json:"relations,omitempty"` } func (x *Memo) Reset() { @@ -206,6 +207,13 @@ func (x *Memo) GetPinned() bool { return false } +func (x *Memo) GetParentId() int32 { + if x != nil && x.ParentId != nil { + return *x.ParentId + } + return 0 +} + func (x *Memo) GetResources() []*Resource { if x != nil { return x.Resources @@ -1416,7 +1424,7 @@ var file_api_v2_memo_service_proto_rawDesc = []byte{ 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd0, 0x04, 0x0a, 0x04, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x80, 0x05, 0x0a, 0x04, 0x4d, 0x65, 0x6d, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x36, 0x0a, 0x0a, 0x72, 0x6f, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, @@ -1446,14 +1454,17 @@ var file_api_v2_memo_service_proto_rawDesc = []byte{ 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x69, - 0x6e, 0x6e, 0x65, 0x64, 0x12, 0x39, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, - 0x03, 0xe0, 0x41, 0x03, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, - 0x3d, 0x0a, 0x09, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0d, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x32, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x03, - 0xe0, 0x41, 0x03, 0x52, 0x09, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x67, + 0x6e, 0x6e, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, + 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, + 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x42, 0x03, 0xe0, 0x41, 0x03, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x12, 0x3d, 0x0a, 0x09, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0e, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x32, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x42, 0x03, 0xe0, 0x41, 0x03, 0x52, 0x09, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x22, 0x67, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x38, 0x0a, @@ -2095,6 +2106,7 @@ func file_api_v2_memo_service_proto_init() { } } } + file_api_v2_memo_service_proto_msgTypes[0].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/store/db/mysql/memo.go b/store/db/mysql/memo.go index 095967406..41c828c71 100644 --- a/store/db/mysql/memo.go +++ b/store/db/mysql/memo.go @@ -55,9 +55,6 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo if v := find.CreatedTsAfter; v != nil { where, args = append(where, "UNIX_TIMESTAMP(`memo`.`created_ts`) > ?"), append(args, *v) } - if v := find.Pinned; v != nil { - where = append(where, "`memo_organizer`.`pinned` = 1") - } if v := find.ContentSearch; len(v) != 0 { for _, s := range v { where, args = append(where, "`memo`.`content` LIKE ?"), append(args, "%"+s+"%") @@ -71,6 +68,10 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo } where = append(where, fmt.Sprintf("`memo`.`visibility` in (%s)", strings.Join(placeholder, ","))) } + if find.ExcludeComments { + where = append(where, "parent_id IS NULL") + } + orders := []string{} if find.OrderByPinned { orders = append(orders, "`pinned` DESC") @@ -91,6 +92,7 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo "`memo`.`content` AS `content`", "`memo`.`visibility` AS `visibility`", "MAX(CASE WHEN `memo_organizer`.`pinned` = 1 THEN 1 ELSE 0 END) AS `pinned`", + "(SELECT `related_memo_id` from `memo_relation` where `memo_id` = `id` AND `type` = \"COMMENT\" LIMIT 1) as `parent_id`", } query := "SELECT " + strings.Join(fields, ",\n") + " FROM `memo` LEFT JOIN `memo_organizer` ON `memo`.`id` = `memo_organizer`.`memo_id` WHERE " + strings.Join(where, " AND ") + " GROUP BY `memo`.`id` ORDER BY " + strings.Join(orders, ", ") if find.Limit != nil { @@ -118,6 +120,7 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo &memo.Content, &memo.Visibility, &memo.Pinned, + &memo.ParentID, ); err != nil { return nil, err } diff --git a/store/db/postgres/memo.go b/store/db/postgres/memo.go index 651a47385..f28b3198c 100644 --- a/store/db/postgres/memo.go +++ b/store/db/postgres/memo.go @@ -59,8 +59,8 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo } where = append(where, fmt.Sprintf("memo.visibility in (%s)", strings.Join(holders, ", "))) } - if v := find.Pinned; v != nil { - where = append(where, "memo_organizer.pinned = 1") + if find.ExcludeComments { + where = append(where, "parent_id IS NULL") } orders := []string{} @@ -82,6 +82,7 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo `memo.row_status AS row_status`, `memo.visibility AS visibility`, `MAX(CASE WHEN memo_organizer.pinned = 1 THEN 1 ELSE 0 END) AS pinned`, + "(SELECT related_memo_id from memo_relation where memo_id = id AND type = 'COMMENT' LIMIT 1) as parent_id", } if !find.ExcludeContent { fields = append(fields, `memo.content AS content`) @@ -117,6 +118,7 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo &memo.RowStatus, &memo.Visibility, &memo.Pinned, + &memo.ParentID, } if !find.ExcludeContent { dests = append(dests, &memo.Content) diff --git a/store/db/sqlite/memo.go b/store/db/sqlite/memo.go index 1e1a3603c..fc3f9afa0 100644 --- a/store/db/sqlite/memo.go +++ b/store/db/sqlite/memo.go @@ -58,8 +58,8 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo } where = append(where, fmt.Sprintf("memo.visibility in (%s)", strings.Join(placeholder, ","))) } - if v := find.Pinned; v != nil { - where = append(where, "memo_organizer.pinned = 1") + if find.ExcludeComments { + where = append(where, "parent_id IS NULL") } orders := []string{} @@ -81,6 +81,7 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo `memo.row_status AS row_status`, `memo.visibility AS visibility`, `CASE WHEN memo_organizer.pinned = 1 THEN 1 ELSE 0 END AS pinned`, + "(SELECT related_memo_id from memo_relation where memo_id = id AND type = \"COMMENT\" LIMIT 1) as parent_id", } if !find.ExcludeContent { fields = append(fields, `memo.content AS content`) @@ -116,6 +117,7 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo &memo.RowStatus, &memo.Visibility, &memo.Pinned, + &memo.ParentID, } if !find.ExcludeContent { dests = append(dests, &memo.Content) diff --git a/store/memo.go b/store/memo.go index d5c45acf0..9307d846d 100644 --- a/store/memo.go +++ b/store/memo.go @@ -42,7 +42,8 @@ type Memo struct { Visibility Visibility // Composed fields - Pinned bool + Pinned bool + ParentID *int32 } type FindMemo struct { @@ -55,10 +56,10 @@ type FindMemo struct { CreatedTsBefore *int64 // Domain specific fields - ContentSearch []string - VisibilityList []Visibility - Pinned *bool - ExcludeContent bool + ContentSearch []string + VisibilityList []Visibility + ExcludeContent bool + ExcludeComments bool // Pagination Limit *int diff --git a/web/src/pages/MemoDetail.tsx b/web/src/pages/MemoDetail.tsx index 7548a48f7..13925ee20 100644 --- a/web/src/pages/MemoDetail.tsx +++ b/web/src/pages/MemoDetail.tsx @@ -62,16 +62,13 @@ const MemoDetail = () => { // Prepare memo comments. useEffect(() => { - if (!memo) { - return; - } - (async () => { - const parentMemoId = memo.relations.find( - (relation) => relation.memoId === memo.id && relation.type === MemoRelation_Type.COMMENT - )?.relatedMemoId; - if (parentMemoId) { - memoStore.getOrFetchMemoById(parentMemoId).then((memo: Memo) => { + if (!memo) { + return; + } + + if (memo.parentId) { + memoStore.getOrFetchMemoById(memo.parentId).then((memo: Memo) => { setParentMemo(memo); }); } else {