refactor: move pinned to memo

pull/4338/head
johnnyjoy 4 months ago
parent 2058a8ab7b
commit d605faeffa

@ -26,7 +26,6 @@ type MemoPayload struct {
Property *MemoPayload_Property `protobuf:"bytes,1,opt,name=property,proto3" json:"property,omitempty"` Property *MemoPayload_Property `protobuf:"bytes,1,opt,name=property,proto3" json:"property,omitempty"`
Location *MemoPayload_Location `protobuf:"bytes,2,opt,name=location,proto3" json:"location,omitempty"` Location *MemoPayload_Location `protobuf:"bytes,2,opt,name=location,proto3" json:"location,omitempty"`
Tags []string `protobuf:"bytes,3,rep,name=tags,proto3" json:"tags,omitempty"` Tags []string `protobuf:"bytes,3,rep,name=tags,proto3" json:"tags,omitempty"`
Pinned bool `protobuf:"varint,4,opt,name=pinned,proto3" json:"pinned,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@ -82,13 +81,6 @@ func (x *MemoPayload) GetTags() []string {
return nil return nil
} }
func (x *MemoPayload) GetPinned() bool {
if x != nil {
return x.Pinned
}
return false
}
// The calculated properties from the memo content. // The calculated properties from the memo content.
type MemoPayload_Property struct { type MemoPayload_Property struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
@ -232,7 +224,7 @@ var File_store_memo_proto protoreflect.FileDescriptor
var file_store_memo_proto_rawDesc = string([]byte{ var file_store_memo_proto_rawDesc = string([]byte{
0x0a, 0x10, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x0a, 0x10, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x12, 0x0b, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x22, 0x74, 0x6f, 0x12, 0x0b, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x22,
0xd8, 0x03, 0x0a, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0xc0, 0x03, 0x0a, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12,
0x3d, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x3d, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e,
0x4d, 0x65, 0x6d, 0x6f, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x4d, 0x65, 0x6d, 0x6f, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x70,
@ -242,36 +234,35 @@ var file_store_memo_proto_rawDesc = string([]byte{
0x65, 0x6d, 0x6f, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x6d, 0x6f, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a,
0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67,
0x73, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x73, 0x1a, 0xb6, 0x01, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x12, 0x19,
0x08, 0x52, 0x06, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x1a, 0xb6, 0x01, 0x0a, 0x08, 0x50, 0x72, 0x0a, 0x08, 0x68, 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x68, 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x52, 0x07, 0x68, 0x61, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x22, 0x0a, 0x0d, 0x68, 0x61, 0x73,
0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x68, 0x61, 0x73, 0x4c, 0x69, 0x6e, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08,
0x6b, 0x12, 0x22, 0x0a, 0x0d, 0x68, 0x61, 0x73, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x6c, 0x69, 0x52, 0x0b, 0x68, 0x61, 0x73, 0x54, 0x61, 0x73, 0x6b, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x19, 0x0a,
0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x68, 0x61, 0x73, 0x54, 0x61, 0x73, 0x08, 0x68, 0x61, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52,
0x6b, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x68, 0x61, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x07, 0x68, 0x61, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x68, 0x61, 0x73, 0x5f,
0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x68, 0x61, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x73,
0x12, 0x30, 0x0a, 0x14, 0x68, 0x61, 0x73, 0x5f, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x68, 0x61, 0x73, 0x49, 0x6e, 0x63, 0x6f, 0x6d,
0x74, 0x65, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65,
0x68, 0x61, 0x73, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x73, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a,
0x6b, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x1a, 0x66, 0x0a, 0x08, 0x4c, 0x6f,
0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x68,
0x65, 0x73, 0x1a, 0x66, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x6c, 0x61,
0x0a, 0x0b, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x74, 0x69,
0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x74, 0x75, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x6c, 0x61, 0x74, 0x69,
0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x74, 0x75, 0x64, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64,
0x28, 0x01, 0x52, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75,
0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x64, 0x65, 0x42, 0x94, 0x01, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73,
0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x42, 0x94, 0x01, 0x0a, 0x0f, 0x63, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x09, 0x4d, 0x65, 0x6d, 0x6f, 0x50, 0x72, 0x6f, 0x74,
0x6f, 0x6d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x09, 0x6f, 0x50, 0x01, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
0x4d, 0x65, 0x6d, 0x6f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x75, 0x73, 0x65, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x70,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x73, 0x65, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0xa2, 0x02,
0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x03, 0x4d, 0x53, 0x58, 0xaa, 0x02, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x53, 0x74, 0x6f,
0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0xa2, 0x02, 0x03, 0x4d, 0x53, 0x58, 0xaa, 0x02, 0x0b, 0x4d, 0x72, 0x65, 0xca, 0x02, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x53, 0x74, 0x6f, 0x72, 0x65,
0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0xca, 0x02, 0x0b, 0x4d, 0x65, 0x6d, 0xe2, 0x02, 0x17, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x5c, 0x47,
0x6f, 0x73, 0x5c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0xe2, 0x02, 0x17, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0c, 0x4d, 0x65, 0x6d,
0x5c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x6f, 0x73, 0x3a, 0x3a, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x74, 0x61, 0xea, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x53, 0x74, 0x6f, 0x72, 0x33,
0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}) })
var ( var (

@ -11,8 +11,6 @@ message MemoPayload {
repeated string tags = 3; repeated string tags = 3;
bool pinned = 4;
// The calculated properties from the memo content. // The calculated properties from the memo content.
message Property { message Property {
bool has_link = 1; bool has_link = 1;

@ -268,6 +268,8 @@ func (s *APIV1Service) UpdateMemo(ctx context.Context, request *v1pb.UpdateMemoR
return nil, status.Errorf(codes.PermissionDenied, "disable public memos system setting is enabled") return nil, status.Errorf(codes.PermissionDenied, "disable public memos system setting is enabled")
} }
update.Visibility = &visibility update.Visibility = &visibility
} else if path == "pinned" {
update.Pinned = &request.Memo.Pinned
} else if path == "state" { } else if path == "state" {
rowStatus := convertStateToStore(request.Memo.State) rowStatus := convertStateToStore(request.Memo.State)
update.RowStatus = &rowStatus update.RowStatus = &rowStatus
@ -291,14 +293,6 @@ func (s *APIV1Service) UpdateMemo(ctx context.Context, request *v1pb.UpdateMemoR
} else { } else {
update.CreatedTs = &displayTs update.CreatedTs = &displayTs
} }
} else if path == "pinned" {
if _, err := s.Store.UpsertMemoOrganizer(ctx, &store.MemoOrganizer{
MemoID: id,
UserID: user.ID,
Pinned: request.Memo.Pinned,
}); err != nil {
return nil, status.Errorf(codes.Internal, "failed to upsert memo organizer")
}
} else if path == "resources" { } else if path == "resources" {
_, err := s.SetMemoResources(ctx, &v1pb.SetMemoResourcesRequest{ _, err := s.SetMemoResources(ctx, &v1pb.SetMemoResourcesRequest{
Name: request.Memo.Name, Name: request.Memo.Name,

@ -138,8 +138,8 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo
"UNIX_TIMESTAMP(`memo`.`updated_ts`) AS `updated_ts`", "UNIX_TIMESTAMP(`memo`.`updated_ts`) AS `updated_ts`",
"`memo`.`row_status` AS `row_status`", "`memo`.`row_status` AS `row_status`",
"`memo`.`visibility` AS `visibility`", "`memo`.`visibility` AS `visibility`",
"`memo`.`pinned` AS `pinned`",
"`memo`.`payload` AS `payload`", "`memo`.`payload` AS `payload`",
"IFNULL(`memo_organizer`.`pinned`, 0) AS `pinned`",
"`memo_relation`.`related_memo_id` AS `parent_id`", "`memo_relation`.`related_memo_id` AS `parent_id`",
} }
if !find.ExcludeContent { if !find.ExcludeContent {
@ -147,7 +147,6 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo
} }
query := "SELECT " + strings.Join(fields, ", ") + " FROM `memo`" + " " + query := "SELECT " + strings.Join(fields, ", ") + " FROM `memo`" + " " +
"LEFT JOIN `memo_organizer` ON `memo`.`id` = `memo_organizer`.`memo_id` AND `memo`.`creator_id` = `memo_organizer`.`user_id`" + " " +
"LEFT JOIN `memo_relation` ON `memo`.`id` = `memo_relation`.`memo_id` AND `memo_relation`.`type` = 'COMMENT'" + " " + "LEFT JOIN `memo_relation` ON `memo`.`id` = `memo_relation`.`memo_id` AND `memo_relation`.`type` = 'COMMENT'" + " " +
"WHERE " + strings.Join(where, " AND ") + " " + "WHERE " + strings.Join(where, " AND ") + " " +
"HAVING " + strings.Join(having, " AND ") + " " + "HAVING " + strings.Join(having, " AND ") + " " +
@ -177,8 +176,8 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo
&memo.UpdatedTs, &memo.UpdatedTs,
&memo.RowStatus, &memo.RowStatus,
&memo.Visibility, &memo.Visibility,
&payloadBytes,
&memo.Pinned, &memo.Pinned,
&payloadBytes,
&memo.ParentID, &memo.ParentID,
} }
if !find.ExcludeContent { if !find.ExcludeContent {
@ -235,6 +234,9 @@ func (d *DB) UpdateMemo(ctx context.Context, update *store.UpdateMemo) error {
if v := update.Visibility; v != nil { if v := update.Visibility; v != nil {
set, args = append(set, "`visibility` = ?"), append(args, *v) set, args = append(set, "`visibility` = ?"), append(args, *v)
} }
if v := update.Pinned; v != nil {
set, args = append(set, "`pinned` = ?"), append(args, *v)
}
if v := update.Payload; v != nil { if v := update.Payload; v != nil {
payloadBytes, err := protojson.Marshal(v) payloadBytes, err := protojson.Marshal(v)
if err != nil { if err != nil {

@ -1,70 +0,0 @@
package mysql
import (
"context"
"strings"
"github.com/usememos/memos/store"
)
func (d *DB) UpsertMemoOrganizer(ctx context.Context, upsert *store.MemoOrganizer) (*store.MemoOrganizer, error) {
stmt := "INSERT INTO `memo_organizer` (`memo_id`, `user_id`, `pinned`) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE `pinned` = ?"
if _, err := d.db.ExecContext(ctx, stmt, upsert.MemoID, upsert.UserID, upsert.Pinned, upsert.Pinned); err != nil {
return nil, err
}
return upsert, nil
}
func (d *DB) ListMemoOrganizer(ctx context.Context, find *store.FindMemoOrganizer) ([]*store.MemoOrganizer, error) {
where, args := []string{"1 = 1"}, []any{}
if find.MemoID != 0 {
where = append(where, "`memo_id` = ?")
args = append(args, find.MemoID)
}
if find.UserID != 0 {
where = append(where, "`user_id` = ?")
args = append(args, find.UserID)
}
query := "SELECT `memo_id`, `user_id`, `pinned` FROM `memo_organizer` WHERE " + strings.Join(where, " AND ")
rows, err := d.db.QueryContext(ctx, query, args...)
if err != nil {
return nil, err
}
defer rows.Close()
list := []*store.MemoOrganizer{}
for rows.Next() {
memoOrganizer := &store.MemoOrganizer{}
if err := rows.Scan(
&memoOrganizer.MemoID,
&memoOrganizer.UserID,
&memoOrganizer.Pinned,
); err != nil {
return nil, err
}
list = append(list, memoOrganizer)
}
if err := rows.Err(); err != nil {
return nil, err
}
return list, nil
}
func (d *DB) DeleteMemoOrganizer(ctx context.Context, delete *store.DeleteMemoOrganizer) error {
where, args := []string{}, []any{}
if v := delete.MemoID; v != nil {
where, args = append(where, "`memo_id` = ?"), append(args, *v)
}
if v := delete.UserID; v != nil {
where, args = append(where, "`user_id` = ?"), append(args, *v)
}
stmt := "DELETE FROM `memo_organizer` WHERE " + strings.Join(where, " AND ")
if _, err := d.db.ExecContext(ctx, stmt, args...); err != nil {
return err
}
return nil
}

@ -129,8 +129,8 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo
`memo.updated_ts AS updated_ts`, `memo.updated_ts AS updated_ts`,
`memo.row_status AS row_status`, `memo.row_status AS row_status`,
`memo.visibility AS visibility`, `memo.visibility AS visibility`,
`memo.pinned AS pinned`,
`memo.payload AS payload`, `memo.payload AS payload`,
`COALESCE(memo_organizer.pinned, 0) AS pinned`,
`memo_relation.related_memo_id AS parent_id`, `memo_relation.related_memo_id AS parent_id`,
} }
if !find.ExcludeContent { if !find.ExcludeContent {
@ -139,7 +139,6 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo
query := `SELECT ` + strings.Join(fields, ", ") + ` query := `SELECT ` + strings.Join(fields, ", ") + `
FROM memo FROM memo
LEFT JOIN memo_organizer ON memo.id = memo_organizer.memo_id AND memo.creator_id = memo_organizer.user_id
LEFT JOIN memo_relation ON memo.id = memo_relation.memo_id AND memo_relation.type = 'COMMENT' LEFT JOIN memo_relation ON memo.id = memo_relation.memo_id AND memo_relation.type = 'COMMENT'
WHERE ` + strings.Join(where, " AND ") + ` WHERE ` + strings.Join(where, " AND ") + `
ORDER BY ` + strings.Join(orders, ", ") ORDER BY ` + strings.Join(orders, ", ")
@ -168,8 +167,8 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo
&memo.UpdatedTs, &memo.UpdatedTs,
&memo.RowStatus, &memo.RowStatus,
&memo.Visibility, &memo.Visibility,
&payloadBytes,
&memo.Pinned, &memo.Pinned,
&payloadBytes,
&memo.ParentID, &memo.ParentID,
} }
if !find.ExcludeContent { if !find.ExcludeContent {
@ -226,6 +225,9 @@ func (d *DB) UpdateMemo(ctx context.Context, update *store.UpdateMemo) error {
if v := update.Visibility; v != nil { if v := update.Visibility; v != nil {
set, args = append(set, "visibility = "+placeholder(len(args)+1)), append(args, *v) set, args = append(set, "visibility = "+placeholder(len(args)+1)), append(args, *v)
} }
if v := update.Pinned; v != nil {
set, args = append(set, "pinned = "+placeholder(len(args)+1)), append(args, *v)
}
if v := update.Payload; v != nil { if v := update.Payload; v != nil {
payloadBytes, err := protojson.Marshal(v) payloadBytes, err := protojson.Marshal(v)
if err != nil { if err != nil {

@ -1,91 +0,0 @@
package postgres
import (
"context"
"fmt"
"strings"
"github.com/usememos/memos/store"
)
func (d *DB) UpsertMemoOrganizer(ctx context.Context, upsert *store.MemoOrganizer) (*store.MemoOrganizer, error) {
pinned := 0
if upsert.Pinned {
pinned = 1
}
stmt := `
INSERT INTO memo_organizer (
memo_id,
user_id,
pinned
)
VALUES (` + placeholders(3) + `)
ON CONFLICT(memo_id, user_id) DO UPDATE
SET pinned = EXCLUDED.pinned`
if _, err := d.db.ExecContext(ctx, stmt, upsert.MemoID, upsert.UserID, pinned); err != nil {
return nil, err
}
return upsert, nil
}
func (d *DB) ListMemoOrganizer(ctx context.Context, find *store.FindMemoOrganizer) ([]*store.MemoOrganizer, error) {
where, args := []string{"1 = 1"}, []any{}
if find.MemoID != 0 {
where, args = append(where, "memo_id = "+placeholder(len(args)+1)), append(args, find.MemoID)
}
if find.UserID != 0 {
where, args = append(where, "user_id = "+placeholder(len(args)+1)), append(args, find.UserID)
}
query := fmt.Sprintf(`
SELECT
memo_id,
user_id,
pinned
FROM memo_organizer
WHERE %s
`, strings.Join(where, " AND "))
rows, err := d.db.QueryContext(ctx, query, args...)
if err != nil {
return nil, err
}
defer rows.Close()
list := []*store.MemoOrganizer{}
for rows.Next() {
memoOrganizer := &store.MemoOrganizer{}
pinned := 0
if err := rows.Scan(
&memoOrganizer.MemoID,
&memoOrganizer.UserID,
&pinned,
); err != nil {
return nil, err
}
memoOrganizer.Pinned = pinned == 1
list = append(list, memoOrganizer)
}
if err := rows.Err(); err != nil {
return nil, err
}
return list, nil
}
func (d *DB) DeleteMemoOrganizer(ctx context.Context, delete *store.DeleteMemoOrganizer) error {
where, args := []string{}, []any{}
if v := delete.MemoID; v != nil {
where, args = append(where, "memo_id = "+placeholder(len(args)+1)), append(args, *v)
}
if v := delete.UserID; v != nil {
where, args = append(where, "user_id = "+placeholder(len(args)+1)), append(args, *v)
}
stmt := `DELETE FROM memo_organizer WHERE ` + strings.Join(where, " AND ")
if _, err := d.db.ExecContext(ctx, stmt, args...); err != nil {
return err
}
return nil
}

@ -130,8 +130,8 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo
"`memo`.`updated_ts` AS `updated_ts`", "`memo`.`updated_ts` AS `updated_ts`",
"`memo`.`row_status` AS `row_status`", "`memo`.`row_status` AS `row_status`",
"`memo`.`visibility` AS `visibility`", "`memo`.`visibility` AS `visibility`",
"`memo`.`pinned` AS `pinned`",
"`memo`.`payload` AS `payload`", "`memo`.`payload` AS `payload`",
"IFNULL(`memo_organizer`.`pinned`, 0) AS `pinned`",
"`memo_relation`.`related_memo_id` AS `parent_id`", "`memo_relation`.`related_memo_id` AS `parent_id`",
} }
if !find.ExcludeContent { if !find.ExcludeContent {
@ -139,7 +139,6 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo
} }
query := "SELECT " + strings.Join(fields, ", ") + "FROM `memo` " + query := "SELECT " + strings.Join(fields, ", ") + "FROM `memo` " +
"LEFT JOIN `memo_organizer` ON `memo`.`id` = `memo_organizer`.`memo_id` AND `memo`.`creator_id` = `memo_organizer`.`user_id` " +
"LEFT JOIN `memo_relation` ON `memo`.`id` = `memo_relation`.`memo_id` AND `memo_relation`.`type` = \"COMMENT\" " + "LEFT JOIN `memo_relation` ON `memo`.`id` = `memo_relation`.`memo_id` AND `memo_relation`.`type` = \"COMMENT\" " +
"WHERE " + strings.Join(where, " AND ") + " " + "WHERE " + strings.Join(where, " AND ") + " " +
"ORDER BY " + strings.Join(orderBy, ", ") "ORDER BY " + strings.Join(orderBy, ", ")
@ -168,8 +167,8 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo
&memo.UpdatedTs, &memo.UpdatedTs,
&memo.RowStatus, &memo.RowStatus,
&memo.Visibility, &memo.Visibility,
&payloadBytes,
&memo.Pinned, &memo.Pinned,
&payloadBytes,
&memo.ParentID, &memo.ParentID,
} }
if !find.ExcludeContent { if !find.ExcludeContent {
@ -213,6 +212,9 @@ func (d *DB) UpdateMemo(ctx context.Context, update *store.UpdateMemo) error {
if v := update.Visibility; v != nil { if v := update.Visibility; v != nil {
set, args = append(set, "`visibility` = ?"), append(args, *v) set, args = append(set, "`visibility` = ?"), append(args, *v)
} }
if v := update.Pinned; v != nil {
set, args = append(set, "`pinned` = ?"), append(args, *v)
}
if v := update.Payload; v != nil { if v := update.Payload; v != nil {
payloadBytes, err := protojson.Marshal(v) payloadBytes, err := protojson.Marshal(v)
if err != nil { if err != nil {

@ -1,89 +0,0 @@
package sqlite
import (
"context"
"fmt"
"strings"
"github.com/usememos/memos/store"
)
func (d *DB) UpsertMemoOrganizer(ctx context.Context, upsert *store.MemoOrganizer) (*store.MemoOrganizer, error) {
stmt := `
INSERT INTO memo_organizer (
memo_id,
user_id,
pinned
)
VALUES (?, ?, ?)
ON CONFLICT(memo_id, user_id) DO UPDATE
SET
pinned = EXCLUDED.pinned
`
if _, err := d.db.ExecContext(ctx, stmt, upsert.MemoID, upsert.UserID, upsert.Pinned); err != nil {
return nil, err
}
return upsert, nil
}
func (d *DB) ListMemoOrganizer(ctx context.Context, find *store.FindMemoOrganizer) ([]*store.MemoOrganizer, error) {
where, args := []string{"1 = 1"}, []any{}
if find.MemoID != 0 {
where = append(where, "memo_id = ?")
args = append(args, find.MemoID)
}
if find.UserID != 0 {
where = append(where, "user_id = ?")
args = append(args, find.UserID)
}
query := fmt.Sprintf(`
SELECT
memo_id,
user_id,
pinned
FROM memo_organizer
WHERE %s
`, strings.Join(where, " AND "))
rows, err := d.db.QueryContext(ctx, query, args...)
if err != nil {
return nil, err
}
defer rows.Close()
list := []*store.MemoOrganizer{}
for rows.Next() {
memoOrganizer := &store.MemoOrganizer{}
if err := rows.Scan(
&memoOrganizer.MemoID,
&memoOrganizer.UserID,
&memoOrganizer.Pinned,
); err != nil {
return nil, err
}
list = append(list, memoOrganizer)
}
if err := rows.Err(); err != nil {
return nil, err
}
return list, nil
}
func (d *DB) DeleteMemoOrganizer(ctx context.Context, delete *store.DeleteMemoOrganizer) error {
where, args := []string{}, []any{}
if v := delete.MemoID; v != nil {
where, args = append(where, "memo_id = ?"), append(args, *v)
}
if v := delete.UserID; v != nil {
where, args = append(where, "user_id = ?"), append(args, *v)
}
stmt := `DELETE FROM memo_organizer WHERE ` + strings.Join(where, " AND ")
if _, err := d.db.ExecContext(ctx, stmt, args...); err != nil {
return err
}
return nil
}

@ -36,11 +36,6 @@ type Driver interface {
ListMemoRelations(ctx context.Context, find *FindMemoRelation) ([]*MemoRelation, error) ListMemoRelations(ctx context.Context, find *FindMemoRelation) ([]*MemoRelation, error)
DeleteMemoRelation(ctx context.Context, delete *DeleteMemoRelation) error DeleteMemoRelation(ctx context.Context, delete *DeleteMemoRelation) error
// MemoOrganizer model related methods.
UpsertMemoOrganizer(ctx context.Context, upsert *MemoOrganizer) (*MemoOrganizer, error)
ListMemoOrganizer(ctx context.Context, find *FindMemoOrganizer) ([]*MemoOrganizer, error)
DeleteMemoOrganizer(ctx context.Context, delete *DeleteMemoOrganizer) error
// WorkspaceSetting model related methods. // WorkspaceSetting model related methods.
UpsertWorkspaceSetting(ctx context.Context, upsert *WorkspaceSetting) (*WorkspaceSetting, error) UpsertWorkspaceSetting(ctx context.Context, upsert *WorkspaceSetting) (*WorkspaceSetting, error)
ListWorkspaceSettings(ctx context.Context, find *FindWorkspaceSetting) ([]*WorkspaceSetting, error) ListWorkspaceSettings(ctx context.Context, find *FindWorkspaceSetting) ([]*WorkspaceSetting, error)

@ -48,10 +48,10 @@ type Memo struct {
// Domain specific fields // Domain specific fields
Content string Content string
Visibility Visibility Visibility Visibility
Pinned bool
Payload *storepb.MemoPayload Payload *storepb.MemoPayload
// Composed fields // Composed fields
Pinned bool
ParentID *int32 ParentID *int32
} }
@ -102,6 +102,7 @@ type UpdateMemo struct {
RowStatus *RowStatus RowStatus *RowStatus
Content *string Content *string
Visibility *Visibility Visibility *Visibility
Pinned *bool
Payload *storepb.MemoPayload Payload *storepb.MemoPayload
} }

@ -1,47 +0,0 @@
package store
import (
"context"
"errors"
)
type MemoOrganizer struct {
MemoID int32
UserID int32
Pinned bool
}
type FindMemoOrganizer struct {
MemoID int32
UserID int32
}
type DeleteMemoOrganizer struct {
MemoID *int32
UserID *int32
}
func (s *Store) UpsertMemoOrganizer(ctx context.Context, upsert *MemoOrganizer) (*MemoOrganizer, error) {
return s.driver.UpsertMemoOrganizer(ctx, upsert)
}
func (s *Store) GetMemoOrganizer(ctx context.Context, find *FindMemoOrganizer) (*MemoOrganizer, error) {
list, err := s.ListMemoOrganizer(ctx, find)
if err != nil {
return nil, err
}
if len(list) == 0 {
return nil, errors.New("not found")
}
return list[0], nil
}
func (s *Store) ListMemoOrganizer(ctx context.Context, find *FindMemoOrganizer) ([]*MemoOrganizer, error) {
return s.driver.ListMemoOrganizer(ctx, find)
}
func (s *Store) DeleteMemoOrganizer(ctx context.Context, delete *DeleteMemoOrganizer) error {
return s.driver.DeleteMemoOrganizer(ctx, delete)
}

@ -47,20 +47,11 @@ CREATE TABLE memo (
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL', row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
content TEXT NOT NULL DEFAULT '', content TEXT NOT NULL DEFAULT '',
visibility TEXT NOT NULL CHECK (visibility IN ('PUBLIC', 'PROTECTED', 'PRIVATE')) DEFAULT 'PRIVATE', visibility TEXT NOT NULL CHECK (visibility IN ('PUBLIC', 'PROTECTED', 'PRIVATE')) DEFAULT 'PRIVATE',
pinned INTEGER NOT NULL CHECK (pinned IN (0, 1)) DEFAULT 0,
payload TEXT NOT NULL DEFAULT '{}' payload TEXT NOT NULL DEFAULT '{}'
); );
CREATE INDEX idx_memo_creator_id ON memo (creator_id); CREATE INDEX idx_memo_creator_id ON memo (creator_id);
CREATE INDEX idx_memo_content ON memo (content);
CREATE INDEX idx_memo_visibility ON memo (visibility);
-- memo_organizer
CREATE TABLE memo_organizer (
memo_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
pinned INTEGER NOT NULL CHECK (pinned IN (0, 1)) DEFAULT 0,
UNIQUE(memo_id, user_id)
);
-- memo_relation -- memo_relation
CREATE TABLE memo_relation ( CREATE TABLE memo_relation (

@ -1,64 +0,0 @@
package teststore
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"github.com/usememos/memos/store"
)
func TestMemoOrganizerStore(t *testing.T) {
ctx := context.Background()
ts := NewTestingStore(ctx, t)
user, err := createTestingHostUser(ctx, ts)
require.NoError(t, err)
memoCreate := &store.Memo{
UID: "main-memo",
CreatorID: user.ID,
Content: "main memo content",
Visibility: store.Public,
}
memo, err := ts.CreateMemo(ctx, memoCreate)
require.NoError(t, err)
require.Equal(t, memoCreate.Content, memo.Content)
memoOrganizer, err := ts.UpsertMemoOrganizer(ctx, &store.MemoOrganizer{
MemoID: memo.ID,
UserID: user.ID,
Pinned: true,
})
require.NoError(t, err)
require.NotNil(t, memoOrganizer)
require.Equal(t, memo.ID, memoOrganizer.MemoID)
require.Equal(t, user.ID, memoOrganizer.UserID)
require.Equal(t, true, memoOrganizer.Pinned)
memoOrganizerTemp, err := ts.GetMemoOrganizer(ctx, &store.FindMemoOrganizer{
MemoID: memo.ID,
})
require.NoError(t, err)
require.Equal(t, memoOrganizer, memoOrganizerTemp)
memoOrganizerTemp, err = ts.UpsertMemoOrganizer(ctx, &store.MemoOrganizer{
MemoID: memo.ID,
UserID: user.ID,
Pinned: false,
})
require.NoError(t, err)
require.NotNil(t, memoOrganizerTemp)
require.Equal(t, memo.ID, memoOrganizerTemp.MemoID)
require.Equal(t, user.ID, memoOrganizerTemp.UserID)
require.Equal(t, false, memoOrganizerTemp.Pinned)
err = ts.DeleteMemoOrganizer(ctx, &store.DeleteMemoOrganizer{
MemoID: &memo.ID,
UserID: &user.ID,
})
require.NoError(t, err)
memoOrganizers, err := ts.ListMemoOrganizer(ctx, &store.FindMemoOrganizer{
UserID: user.ID,
})
require.NoError(t, err)
require.Equal(t, 0, len(memoOrganizers))
ts.Close()
}
Loading…
Cancel
Save