From cdc27c7feeba923670e018f01f1786eb04079010 Mon Sep 17 00:00:00 2001 From: dangjinghao Date: Sat, 13 Sep 2025 10:31:42 +0000 Subject: [PATCH 1/4] fix: wrong shebang in `build.sh` --- scripts/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build.sh b/scripts/build.sh index 5340941a7..8c3f3119b 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Exit when any command fails set -e From 49754ef2e8dd8165f611175cc0af23afb491c620 Mon Sep 17 00:00:00 2001 From: dangjinghao Date: Sun, 14 Sep 2025 07:13:33 +0000 Subject: [PATCH 2/4] feat: backend `ListMemos` API supports return comments upd: rename/delete tags regardless of whether they are in comments or not --- proto/api/v1/memo_service.proto | 3 +++ proto/gen/api/v1/memo_service.pb.go | 16 +++++++++++++--- proto/gen/openapi.yaml | 5 +++++ server/router/api/v1/memo_service.go | 6 +++--- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/proto/api/v1/memo_service.proto b/proto/api/v1/memo_service.proto index 3a9bb4612..cfd62f017 100644 --- a/proto/api/v1/memo_service.proto +++ b/proto/api/v1/memo_service.proto @@ -301,6 +301,9 @@ message ListMemosRequest { // Optional. If true, show deleted memos in the response. bool show_deleted = 6 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. If true, include comment memos in the response. + bool show_comments = 7 [(google.api.field_behavior) = OPTIONAL]; } message ListMemosResponse { diff --git a/proto/gen/api/v1/memo_service.pb.go b/proto/gen/api/v1/memo_service.pb.go index 667327bea..071ba8768 100644 --- a/proto/gen/api/v1/memo_service.pb.go +++ b/proto/gen/api/v1/memo_service.pb.go @@ -571,7 +571,9 @@ type ListMemosRequest struct { // Refer to `Shortcut.filter`. Filter string `protobuf:"bytes,5,opt,name=filter,proto3" json:"filter,omitempty"` // Optional. If true, show deleted memos in the response. - ShowDeleted bool `protobuf:"varint,6,opt,name=show_deleted,json=showDeleted,proto3" json:"show_deleted,omitempty"` + ShowDeleted bool `protobuf:"varint,6,opt,name=show_deleted,json=showDeleted,proto3" json:"show_deleted,omitempty"` + // Optional. If true, include comment memos in the response. + ShowComments bool `protobuf:"varint,7,opt,name=show_comments,json=showComments,proto3" json:"show_comments,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -648,6 +650,13 @@ func (x *ListMemosRequest) GetShowDeleted() bool { return false } +func (x *ListMemosRequest) GetShowComments() bool { + if x != nil { + return x.ShowComments + } + return false +} + type ListMemosResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // The list of memos. @@ -2053,7 +2062,7 @@ const file_api_v1_memo_service_proto_rawDesc = "" + "\amemo_id\x18\x02 \x01(\tB\x03\xe0A\x01R\x06memoId\x12(\n" + "\rvalidate_only\x18\x03 \x01(\bB\x03\xe0A\x01R\fvalidateOnly\x12\"\n" + "\n" + - "request_id\x18\x04 \x01(\tB\x03\xe0A\x01R\trequestId\"\xed\x01\n" + + "request_id\x18\x04 \x01(\tB\x03\xe0A\x01R\trequestId\"\x97\x02\n" + "\x10ListMemosRequest\x12 \n" + "\tpage_size\x18\x01 \x01(\x05B\x03\xe0A\x01R\bpageSize\x12\"\n" + "\n" + @@ -2061,7 +2070,8 @@ const file_api_v1_memo_service_proto_rawDesc = "" + "\x05state\x18\x03 \x01(\x0e2\x13.memos.api.v1.StateB\x03\xe0A\x01R\x05state\x12\x1e\n" + "\border_by\x18\x04 \x01(\tB\x03\xe0A\x01R\aorderBy\x12\x1b\n" + "\x06filter\x18\x05 \x01(\tB\x03\xe0A\x01R\x06filter\x12&\n" + - "\fshow_deleted\x18\x06 \x01(\bB\x03\xe0A\x01R\vshowDeleted\"\x84\x01\n" + + "\fshow_deleted\x18\x06 \x01(\bB\x03\xe0A\x01R\vshowDeleted\x12(\n" + + "\rshow_comments\x18\a \x01(\bB\x03\xe0A\x01R\fshowComments\"\x84\x01\n" + "\x11ListMemosResponse\x12(\n" + "\x05memos\x18\x01 \x03(\v2\x12.memos.api.v1.MemoR\x05memos\x12&\n" + "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\x12\x1d\n" + diff --git a/proto/gen/openapi.yaml b/proto/gen/openapi.yaml index ad86bc3a9..1dd7f9af8 100644 --- a/proto/gen/openapi.yaml +++ b/proto/gen/openapi.yaml @@ -620,6 +620,11 @@ paths: description: Optional. If true, show deleted memos in the response. schema: type: boolean + - name: showComments + in: query + description: Optional. If true, include comment memos in the response. + schema: + type: boolean responses: "200": description: OK diff --git a/server/router/api/v1/memo_service.go b/server/router/api/v1/memo_service.go index 57f6bfab8..2205ae8b5 100644 --- a/server/router/api/v1/memo_service.go +++ b/server/router/api/v1/memo_service.go @@ -108,7 +108,7 @@ func (s *APIV1Service) CreateMemo(ctx context.Context, request *v1pb.CreateMemoR func (s *APIV1Service) ListMemos(ctx context.Context, request *v1pb.ListMemosRequest) (*v1pb.ListMemosResponse, error) { memoFind := &store.FindMemo{ // Exclude comments by default. - ExcludeComments: true, + ExcludeComments: !request.ShowComments, } if request.State == v1pb.State_ARCHIVED { state := store.Archived @@ -695,7 +695,7 @@ func (s *APIV1Service) RenameMemoTag(ctx context.Context, request *v1pb.RenameMe memoFind := &store.FindMemo{ CreatorID: &user.ID, Filters: []string{fmt.Sprintf("tag in [\"%s\"]", request.OldTag)}, - ExcludeComments: true, + ExcludeComments: false, } if (request.Parent) != "memos/-" { memoUID, err := ExtractMemoUIDFromName(request.Parent) @@ -746,7 +746,7 @@ func (s *APIV1Service) DeleteMemoTag(ctx context.Context, request *v1pb.DeleteMe CreatorID: &user.ID, Filters: []string{fmt.Sprintf("tag in [\"%s\"]", request.Tag)}, ExcludeContent: true, - ExcludeComments: true, + ExcludeComments: false, } if request.Parent != "memos/-" { memoUID, err := ExtractMemoUIDFromName(request.Parent) From 77524b78715fb301a554cb1b6930d509f38a03b0 Mon Sep 17 00:00:00 2001 From: dangjinghao Date: Sun, 14 Sep 2025 07:29:19 +0000 Subject: [PATCH 3/4] feat: frontend supports `Show comments` display setting --- web/src/components/MemoDisplaySettingMenu.tsx | 13 ++++++++++ .../PagedMemoList/PagedMemoList.tsx | 4 +++- web/src/locales/en.json | 1 + web/src/pages/Archived.tsx | 2 ++ web/src/pages/Explore.tsx | 1 + web/src/pages/Home.tsx | 1 + web/src/pages/UserProfile.tsx | 1 + web/src/store/view.ts | 4 ++++ web/src/types/proto/api/v1/memo_service.ts | 24 ++++++++++++++++++- 9 files changed, 49 insertions(+), 2 deletions(-) diff --git a/web/src/components/MemoDisplaySettingMenu.tsx b/web/src/components/MemoDisplaySettingMenu.tsx index e906913fc..317afc4a9 100644 --- a/web/src/components/MemoDisplaySettingMenu.tsx +++ b/web/src/components/MemoDisplaySettingMenu.tsx @@ -1,5 +1,6 @@ import { Settings2Icon } from "lucide-react"; import { observer } from "mobx-react-lite"; +import { Checkbox } from "@/components/ui/checkbox"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { cn } from "@/lib/utils"; import { viewStore } from "@/store"; @@ -59,6 +60,18 @@ const MemoDisplaySettingMenu = observer(({ className }: Props) => { +
+ {t("common.show-comments")} + + viewStore.state.setPartial({ + showComments: Boolean(value), + }) + } + /> +
diff --git a/web/src/components/PagedMemoList/PagedMemoList.tsx b/web/src/components/PagedMemoList/PagedMemoList.tsx index c99023a66..17b7518ed 100644 --- a/web/src/components/PagedMemoList/PagedMemoList.tsx +++ b/web/src/components/PagedMemoList/PagedMemoList.tsx @@ -20,6 +20,7 @@ interface Props { listSort?: (list: Memo[]) => Memo[]; state?: State; orderBy?: string; + showComments?: boolean; filter?: string; pageSize?: number; } @@ -49,6 +50,7 @@ const PagedMemoList = observer((props: Props) => { const response = await memoStore.fetchMemos({ state: props.state || State.NORMAL, orderBy: props.orderBy || "display_time desc", + showComments: props.showComments, filter: props.filter, pageSize: props.pageSize || DEFAULT_LIST_MEMOS_PAGE_SIZE, pageToken, @@ -99,7 +101,7 @@ const PagedMemoList = observer((props: Props) => { // Initial load and reload when props change useEffect(() => { refreshList(); - }, [props.state, props.orderBy, props.filter, props.pageSize]); + }, [props.state, props.orderBy, props.filter, props.pageSize, props.showComments]); // Auto-fetch more content when list changes and page isn't full useEffect(() => { diff --git a/web/src/locales/en.json b/web/src/locales/en.json index d3833b9a6..dc1ed1c24 100644 --- a/web/src/locales/en.json +++ b/web/src/locales/en.json @@ -52,6 +52,7 @@ "language": "Language", "last-updated-at": "Last updated at", "layout": "Layout", + "show-comments": "Show comments", "learn-more": "Learn more", "link": "Link", "mark": "Mark", diff --git a/web/src/pages/Archived.tsx b/web/src/pages/Archived.tsx index 20c1d4ef0..43e7e1d74 100644 --- a/web/src/pages/Archived.tsx +++ b/web/src/pages/Archived.tsx @@ -39,6 +39,8 @@ const Archived = observer(() => { } state={State.ARCHIVED} orderBy={viewStore.state.orderByTimeAsc ? "display_time asc" : "display_time desc"} + // It does not support show comments now + // showComments={viewStore.state.showComments} filter={memoFitler} /> ); diff --git a/web/src/pages/Explore.tsx b/web/src/pages/Explore.tsx index f5cc4eeed..dddc690e4 100644 --- a/web/src/pages/Explore.tsx +++ b/web/src/pages/Explore.tsx @@ -27,6 +27,7 @@ const Explore = observer(() => { ) } orderBy={viewStore.state.orderByTimeAsc ? "display_time asc" : "display_time desc"} + showComments={viewStore.state.showComments} /> diff --git a/web/src/pages/Home.tsx b/web/src/pages/Home.tsx index 49d0b792f..6369f7838 100644 --- a/web/src/pages/Home.tsx +++ b/web/src/pages/Home.tsx @@ -67,6 +67,7 @@ const Home = observer(() => { ) } orderBy={viewStore.state.orderByTimeAsc ? "display_time asc" : "display_time desc"} + showComments={viewStore.state.showComments} filter={memoFilter} /> diff --git a/web/src/pages/UserProfile.tsx b/web/src/pages/UserProfile.tsx index 8e0528fe1..713edd455 100644 --- a/web/src/pages/UserProfile.tsx +++ b/web/src/pages/UserProfile.tsx @@ -102,6 +102,7 @@ const UserProfile = observer(() => { ) } orderBy={viewStore.state.orderByTimeAsc ? "display_time asc" : "display_time desc"} + showComments={viewStore.state.showComments} filter={memoFilter} /> diff --git a/web/src/store/view.ts b/web/src/store/view.ts index a8535ad1e..cb7a44cc0 100644 --- a/web/src/store/view.ts +++ b/web/src/store/view.ts @@ -5,6 +5,7 @@ const LOCAL_STORAGE_KEY = "memos-view-setting"; class LocalState { orderByTimeAsc: boolean = false; layout: "LIST" | "MASONRY" = "LIST"; + showComments: boolean = false; constructor() { makeAutoObservable(this); @@ -41,6 +42,9 @@ const viewStore = (() => { viewStore.state.setPartial({ layout: cache.layout }); } } + if (Object.hasOwn(cache, "showComments")) { + viewStore.state.setPartial({ showComments: Boolean(cache.showComments) }); + } } catch { // Do nothing } diff --git a/web/src/types/proto/api/v1/memo_service.ts b/web/src/types/proto/api/v1/memo_service.ts index 1f6406f81..848eb3f96 100644 --- a/web/src/types/proto/api/v1/memo_service.ts +++ b/web/src/types/proto/api/v1/memo_service.ts @@ -206,6 +206,8 @@ export interface ListMemosRequest { filter: string; /** Optional. If true, show deleted memos in the response. */ showDeleted: boolean; + /** Optional. If true, include comment memos in the response. */ + showComments: boolean; } export interface ListMemosResponse { @@ -1084,7 +1086,15 @@ export const CreateMemoRequest: MessageFns = { }; function createBaseListMemosRequest(): ListMemosRequest { - return { pageSize: 0, pageToken: "", state: State.STATE_UNSPECIFIED, orderBy: "", filter: "", showDeleted: false }; + return { + pageSize: 0, + pageToken: "", + state: State.STATE_UNSPECIFIED, + orderBy: "", + filter: "", + showDeleted: false, + showComments: false, + }; } export const ListMemosRequest: MessageFns = { @@ -1107,6 +1117,9 @@ export const ListMemosRequest: MessageFns = { if (message.showDeleted !== false) { writer.uint32(48).bool(message.showDeleted); } + if (message.showComments !== false) { + writer.uint32(56).bool(message.showComments); + } return writer; }, @@ -1165,6 +1178,14 @@ export const ListMemosRequest: MessageFns = { message.showDeleted = reader.bool(); continue; } + case 7: { + if (tag !== 56) { + break; + } + + message.showComments = reader.bool(); + continue; + } } if ((tag & 7) === 4 || tag === 0) { break; @@ -1185,6 +1206,7 @@ export const ListMemosRequest: MessageFns = { message.orderBy = object.orderBy ?? ""; message.filter = object.filter ?? ""; message.showDeleted = object.showDeleted ?? false; + message.showComments = object.showComments ?? false; return message; }, }; From 55c12c2a35c3ba624c364e8a364d07efdfcaf2d9 Mon Sep 17 00:00:00 2001 From: dangjinghao Date: Sun, 14 Sep 2025 07:55:38 +0000 Subject: [PATCH 4/4] fix: count and search tags in comments (#4671) --- server/router/api/v1/memo_service.go | 1 - server/router/api/v1/user_service_stats.go | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/server/router/api/v1/memo_service.go b/server/router/api/v1/memo_service.go index 2205ae8b5..e55523385 100644 --- a/server/router/api/v1/memo_service.go +++ b/server/router/api/v1/memo_service.go @@ -107,7 +107,6 @@ func (s *APIV1Service) CreateMemo(ctx context.Context, request *v1pb.CreateMemoR func (s *APIV1Service) ListMemos(ctx context.Context, request *v1pb.ListMemosRequest) (*v1pb.ListMemosResponse, error) { memoFind := &store.FindMemo{ - // Exclude comments by default. ExcludeComments: !request.ShowComments, } if request.State == v1pb.State_ARCHIVED { diff --git a/server/router/api/v1/user_service_stats.go b/server/router/api/v1/user_service_stats.go index 95583aceb..e95cab2a5 100644 --- a/server/router/api/v1/user_service_stats.go +++ b/server/router/api/v1/user_service_stats.go @@ -22,8 +22,7 @@ func (s *APIV1Service) ListAllUserStats(ctx context.Context, _ *v1pb.ListAllUser normalStatus := store.Normal memoFind := &store.FindMemo{ - // Exclude comments by default. - ExcludeComments: true, + ExcludeComments: false, ExcludeContent: true, RowStatus: &normalStatus, } @@ -83,9 +82,8 @@ func (s *APIV1Service) GetUserStats(ctx context.Context, request *v1pb.GetUserSt normalStatus := store.Normal memoFind := &store.FindMemo{ - CreatorID: &userID, - // Exclude comments by default. - ExcludeComments: true, + CreatorID: &userID, + ExcludeComments: false, ExcludeContent: true, RowStatus: &normalStatus, }