From 6b507ff6002d32cb1f5782d7cd138cc354b9f891 Mon Sep 17 00:00:00 2001 From: Neo Date: Sun, 10 Aug 2025 15:22:54 +0900 Subject: [PATCH 1/3] fix: pinned shortcut comparison operators (#4987) --- plugin/filter/common_converter.go | 35 +++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/plugin/filter/common_converter.go b/plugin/filter/common_converter.go index 73c693911..6ad0a04c3 100644 --- a/plugin/filter/common_converter.go +++ b/plugin/filter/common_converter.go @@ -122,7 +122,7 @@ func (c *CommonSQLConverter) handleComparisonOperator(ctx *ConvertContext, callE return err } - if !slices.Contains([]string{"creator_id", "created_ts", "updated_ts", "visibility", "content", "has_task_list", "has_link", "has_code", "has_incomplete_tasks"}, identifier) { + if !slices.Contains([]string{"creator_id", "created_ts", "updated_ts", "visibility", "content", "pinned", "has_task_list", "has_link", "has_code", "has_incomplete_tasks"}, identifier) { return errors.Errorf("invalid identifier for %s", callExpr.Function) } @@ -140,6 +140,8 @@ func (c *CommonSQLConverter) handleComparisonOperator(ctx *ConvertContext, callE return c.handleStringComparison(ctx, identifier, operator, value) case "creator_id": return c.handleIntComparison(ctx, identifier, operator, value) + case "pinned": + return c.handlePinnedComparison(ctx, operator, value) case "has_task_list", "has_link", "has_code", "has_incomplete_tasks": return c.handleBooleanComparison(ctx, identifier, operator, value) } @@ -491,6 +493,35 @@ func (c *CommonSQLConverter) handleIntComparison(ctx *ConvertContext, field, ope return nil } +func (c *CommonSQLConverter) handlePinnedComparison(ctx *ConvertContext, operator string, value interface{}) error { + if operator != "=" && operator != "!=" { + return errors.Errorf("invalid operator for pinned field") + } + + valueBool, ok := value.(bool) + if !ok { + return errors.New("invalid boolean value for pinned field") + } + + tablePrefix := c.dialect.GetTablePrefix("memo") + + var sqlExpr string + if _, ok := c.dialect.(*PostgreSQLDialect); ok { + sqlExpr = fmt.Sprintf("%s.pinned %s %s", tablePrefix, operator, c.dialect.GetParameterPlaceholder(c.paramIndex)) + } else { + sqlExpr = fmt.Sprintf("%s.`pinned` %s %s", tablePrefix, operator, c.dialect.GetParameterPlaceholder(c.paramIndex)) + } + + if _, err := ctx.Buffer.WriteString(sqlExpr); err != nil { + return err + } + + ctx.Args = append(ctx.Args, c.dialect.GetBooleanValue(valueBool)) + c.paramIndex++ + + return nil +} + func (c *CommonSQLConverter) handleBooleanComparison(ctx *ConvertContext, field, operator string, value interface{}) error { if operator != "=" && operator != "!=" { return errors.Errorf("invalid operator for %s", field) @@ -574,7 +605,7 @@ func (c *CommonSQLConverter) handleBooleanComparison(ctx *ConvertContext, field, // Handle PostgreSQL differently - it uses the raw operator if _, ok := c.dialect.(*PostgreSQLDialect); ok { - var jsonExtract = c.dialect.GetJSONExtract(jsonPath) + jsonExtract := c.dialect.GetJSONExtract(jsonPath) sqlExpr := fmt.Sprintf("(%s)::boolean %s %s", jsonExtract, From 1dd25634fd1faf544c691f9abe03d26a61b1c5c0 Mon Sep 17 00:00:00 2001 From: Jason Shawn D' Souza Date: Sun, 10 Aug 2025 07:23:41 +0100 Subject: [PATCH 2/3] fix: Midi files show up as playable (#4991) --- web/src/components/MemoAttachment.tsx | 4 ++-- web/src/utils/attachment.ts | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/web/src/components/MemoAttachment.tsx b/web/src/components/MemoAttachment.tsx index 825af5eb3..2c6e318b5 100644 --- a/web/src/components/MemoAttachment.tsx +++ b/web/src/components/MemoAttachment.tsx @@ -1,5 +1,5 @@ import { Attachment } from "@/types/proto/api/v1/attachment_service"; -import { getAttachmentUrl } from "@/utils/attachment"; +import { getAttachmentUrl, isMidiFile } from "@/utils/attachment"; import AttachmentIcon from "./AttachmentIcon"; interface Props { @@ -19,7 +19,7 @@ const MemoAttachment: React.FC = (props: Props) => {
- {attachment.type.startsWith("audio") ? ( + {attachment.type.startsWith("audio") && !isMidiFile(attachment.type) ? ( ) : ( <> diff --git a/web/src/utils/attachment.ts b/web/src/utils/attachment.ts index 8db275849..cf6ab9f4f 100644 --- a/web/src/utils/attachment.ts +++ b/web/src/utils/attachment.ts @@ -13,7 +13,7 @@ export const getAttachmentType = (attachment: Attachment) => { return "image/*"; } else if (attachment.type.startsWith("video")) { return "video/*"; - } else if (attachment.type.startsWith("audio")) { + } else if (attachment.type.startsWith("audio") && !isMidiFile(attachment.type)) { return "audio/*"; } else if (attachment.type.startsWith("text")) { return "text/*"; @@ -40,6 +40,11 @@ export const isImage = (t: string) => { return t.startsWith("image/") && !isPSD(t); }; +// isMidiFile returns true if the given mime type is a MIDI file. +export const isMidiFile = (mimeType: string): boolean => { + return mimeType === "audio/midi" || mimeType === "audio/mid" || mimeType === "audio/x-midi" || mimeType === "application/x-midi"; +}; + const isPSD = (t: string) => { return t === "image/vnd.adobe.photoshop" || t === "image/x-photoshop" || t === "image/photoshop"; }; From c76ffb0fe4dc5edf0b1e71bca24620df7cdc0cc5 Mon Sep 17 00:00:00 2001 From: Neo Date: Sun, 10 Aug 2025 15:25:35 +0900 Subject: [PATCH 3/3] chore: adds a check to only query reactions when there are actual memos (#4984) --- server/router/api/v1/memo_service.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/server/router/api/v1/memo_service.go b/server/router/api/v1/memo_service.go index 41f6f238c..85ce0748d 100644 --- a/server/router/api/v1/memo_service.go +++ b/server/router/api/v1/memo_service.go @@ -186,15 +186,17 @@ func (s *APIV1Service) ListMemos(ctx context.Context, request *v1pb.ListMemosReq memoNames = append(memoNames, fmt.Sprintf("'%s/%s'", MemoNamePrefix, m.UID)) } - reactions, err := s.Store.ListReactions(ctx, &store.FindReaction{ - Filters: []string{fmt.Sprintf("content_id in [%s]", strings.Join(memoNames, ", "))}, - }) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to list reactions") - } + if len(memoNames) > 0 { + reactions, err := s.Store.ListReactions(ctx, &store.FindReaction{ + Filters: []string{fmt.Sprintf("content_id in [%s]", strings.Join(memoNames, ", "))}, + }) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to list reactions") + } - for _, reaction := range reactions { - reactionMap[reaction.ContentID] = append(reactionMap[reaction.ContentID], reaction) + for _, reaction := range reactions { + reactionMap[reaction.ContentID] = append(reactionMap[reaction.ContentID], reaction) + } } for _, memo := range memos {