diff --git a/proto/api/v1/activity_service.proto b/proto/api/v1/activity_service.proto index 25ded5c15..97e2b91cb 100644 --- a/proto/api/v1/activity_service.proto +++ b/proto/api/v1/activity_service.proto @@ -61,8 +61,6 @@ message Activity { TYPE_UNSPECIFIED = 0; // Memo comment activity. MEMO_COMMENT = 1; - // Version update activity. - VERSION_UPDATE = 2; } // Activity levels. diff --git a/proto/api/v1/inbox_service.proto b/proto/api/v1/inbox_service.proto deleted file mode 100644 index 4571a86b3..000000000 --- a/proto/api/v1/inbox_service.proto +++ /dev/null @@ -1,149 +0,0 @@ -syntax = "proto3"; - -package memos.api.v1; - -import "google/api/annotations.proto"; -import "google/api/client.proto"; -import "google/api/field_behavior.proto"; -import "google/api/resource.proto"; -import "google/protobuf/empty.proto"; -import "google/protobuf/field_mask.proto"; -import "google/protobuf/timestamp.proto"; - -option go_package = "gen/api/v1"; - -service InboxService { - // ListInboxes lists inboxes for a user. - rpc ListInboxes(ListInboxesRequest) returns (ListInboxesResponse) { - option (google.api.http) = {get: "/api/v1/{parent=users/*}/inboxes"}; - option (google.api.method_signature) = "parent"; - } - // UpdateInbox updates an inbox. - rpc UpdateInbox(UpdateInboxRequest) returns (Inbox) { - option (google.api.http) = { - patch: "/api/v1/{inbox.name=inboxes/*}" - body: "inbox" - }; - option (google.api.method_signature) = "inbox,update_mask"; - } - // DeleteInbox deletes an inbox. - rpc DeleteInbox(DeleteInboxRequest) returns (google.protobuf.Empty) { - option (google.api.http) = {delete: "/api/v1/{name=inboxes/*}"}; - option (google.api.method_signature) = "name"; - } -} - -message Inbox { - option (google.api.resource) = { - type: "memos.api.v1/Inbox" - pattern: "inboxes/{inbox}" - name_field: "name" - singular: "inbox" - plural: "inboxes" - }; - - // The resource name of the inbox. - // Format: inboxes/{inbox} - string name = 1 [(google.api.field_behavior) = IDENTIFIER]; - - // The sender of the inbox notification. - // Format: users/{user} - string sender = 2 [(google.api.field_behavior) = OUTPUT_ONLY]; - - // The receiver of the inbox notification. - // Format: users/{user} - string receiver = 3 [(google.api.field_behavior) = OUTPUT_ONLY]; - - // The status of the inbox notification. - Status status = 4 [(google.api.field_behavior) = OPTIONAL]; - - // Output only. The creation timestamp. - google.protobuf.Timestamp create_time = 5 [(google.api.field_behavior) = OUTPUT_ONLY]; - - // The type of the inbox notification. - Type type = 6 [(google.api.field_behavior) = OUTPUT_ONLY]; - - // Optional. The activity ID associated with this inbox notification. - optional int32 activity_id = 7 [(google.api.field_behavior) = OPTIONAL]; - - // Status enumeration for inbox notifications. - enum Status { - // Unspecified status. - STATUS_UNSPECIFIED = 0; - // The notification is unread. - UNREAD = 1; - // The notification is archived. - ARCHIVED = 2; - } - - // Type enumeration for inbox notifications. - enum Type { - // Unspecified type. - TYPE_UNSPECIFIED = 0; - // Memo comment notification. - MEMO_COMMENT = 1; - // Version update notification. - VERSION_UPDATE = 2; - } -} - -message ListInboxesRequest { - // Required. The parent resource whose inboxes will be listed. - // Format: users/{user} - string parent = 1 [ - (google.api.field_behavior) = REQUIRED, - (google.api.resource_reference) = {type: "memos.api.v1/User"} - ]; - - // Optional. The maximum number of inboxes to return. - // The service may return fewer than this value. - // If unspecified, at most 50 inboxes will be returned. - // The maximum value is 1000; values above 1000 will be coerced to 1000. - int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL]; - - // Optional. A page token, received from a previous `ListInboxes` call. - // Provide this to retrieve the subsequent page. - string page_token = 3 [(google.api.field_behavior) = OPTIONAL]; - - // Optional. Filter to apply to the list results. - // Example: "status=UNREAD" or "type=MEMO_COMMENT" - // Supported operators: =, != - // Supported fields: status, type, sender, create_time - string filter = 4 [(google.api.field_behavior) = OPTIONAL]; - - // Optional. The order to sort results by. - // Example: "create_time desc" or "status asc" - string order_by = 5 [(google.api.field_behavior) = OPTIONAL]; -} - -message ListInboxesResponse { - // The list of inboxes. - repeated Inbox inboxes = 1; - - // A token that can be sent as `page_token` to retrieve the next page. - // If this field is omitted, there are no subsequent pages. - string next_page_token = 2; - - // The total count of inboxes (may be approximate). - int32 total_size = 3; -} - -message UpdateInboxRequest { - // Required. The inbox to update. - Inbox inbox = 1 [(google.api.field_behavior) = REQUIRED]; - - // Required. The list of fields to update. - google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = REQUIRED]; - - // Optional. If set to true, allows updating missing fields. - bool allow_missing = 3 [(google.api.field_behavior) = OPTIONAL]; -} - -message DeleteInboxRequest { - // Required. The resource name of the inbox to delete. - // Format: inboxes/{inbox} - string name = 1 [ - (google.api.field_behavior) = REQUIRED, - (google.api.resource_reference) = {type: "memos.api.v1/Inbox"} - ]; -} diff --git a/proto/api/v1/user_service.proto b/proto/api/v1/user_service.proto index 8ed5376b3..1c14f45f0 100644 --- a/proto/api/v1/user_service.proto +++ b/proto/api/v1/user_service.proto @@ -153,6 +153,27 @@ service UserService { option (google.api.http) = {delete: "/api/v1/{name=users/*/webhooks/*}"}; option (google.api.method_signature) = "name"; } + + // ListUserNotifications lists notifications for a user. + rpc ListUserNotifications(ListUserNotificationsRequest) returns (ListUserNotificationsResponse) { + option (google.api.http) = {get: "/api/v1/{parent=users/*}/notifications"}; + option (google.api.method_signature) = "parent"; + } + + // UpdateUserNotification updates a notification. + rpc UpdateUserNotification(UpdateUserNotificationRequest) returns (UserNotification) { + option (google.api.http) = { + patch: "/api/v1/{notification.name=users/*/notifications/*}" + body: "notification" + }; + option (google.api.method_signature) = "notification,update_mask"; + } + + // DeleteUserNotification deletes a notification. + rpc DeleteUserNotification(DeleteUserNotificationRequest) returns (google.protobuf.Empty) { + option (google.api.http) = {delete: "/api/v1/{name=users/*/notifications/*}"}; + option (google.api.method_signature) = "name"; + } } message User { @@ -672,3 +693,81 @@ message DeleteUserWebhookRequest { // Format: users/{user}/webhooks/{webhook} string name = 1 [(google.api.field_behavior) = REQUIRED]; } + +message UserNotification { + option (google.api.resource) = { + type: "memos.api.v1/UserNotification" + pattern: "users/{user}/notifications/{notification}" + name_field: "name" + singular: "notification" + plural: "notifications" + }; + + // The resource name of the notification. + // Format: users/{user}/notifications/{notification} + string name = 1 [ + (google.api.field_behavior) = OUTPUT_ONLY, + (google.api.field_behavior) = IDENTIFIER + ]; + + // The sender of the notification. + // Format: users/{user} + string sender = 2 [ + (google.api.field_behavior) = OUTPUT_ONLY, + (google.api.resource_reference) = {type: "memos.api.v1/User"} + ]; + + // The status of the notification. + Status status = 3 [(google.api.field_behavior) = OPTIONAL]; + + // The creation timestamp. + google.protobuf.Timestamp create_time = 4 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // The type of the notification. + Type type = 5 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // The activity ID associated with this notification. + optional int32 activity_id = 6 [(google.api.field_behavior) = OPTIONAL]; + + enum Status { + STATUS_UNSPECIFIED = 0; + UNREAD = 1; + ARCHIVED = 2; + } + + enum Type { + TYPE_UNSPECIFIED = 0; + MEMO_COMMENT = 1; + } +} + +message ListUserNotificationsRequest { + // The parent user resource. + // Format: users/{user} + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = {type: "memos.api.v1/User"} + ]; + + int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL]; + string page_token = 3 [(google.api.field_behavior) = OPTIONAL]; + string filter = 4 [(google.api.field_behavior) = OPTIONAL]; +} + +message ListUserNotificationsResponse { + repeated UserNotification notifications = 1; + string next_page_token = 2; +} + +message UpdateUserNotificationRequest { + UserNotification notification = 1 [(google.api.field_behavior) = REQUIRED]; + google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = REQUIRED]; +} + +message DeleteUserNotificationRequest { + // Format: users/{user}/notifications/{notification} + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = {type: "memos.api.v1/UserNotification"} + ]; +} diff --git a/proto/gen/api/v1/activity_service.pb.go b/proto/gen/api/v1/activity_service.pb.go index 87530f29c..67824c087 100644 --- a/proto/gen/api/v1/activity_service.pb.go +++ b/proto/gen/api/v1/activity_service.pb.go @@ -31,8 +31,6 @@ const ( Activity_TYPE_UNSPECIFIED Activity_Type = 0 // Memo comment activity. Activity_MEMO_COMMENT Activity_Type = 1 - // Version update activity. - Activity_VERSION_UPDATE Activity_Type = 2 ) // Enum value maps for Activity_Type. @@ -40,12 +38,10 @@ var ( Activity_Type_name = map[int32]string{ 0: "TYPE_UNSPECIFIED", 1: "MEMO_COMMENT", - 2: "VERSION_UPDATE", } Activity_Type_value = map[string]int32{ "TYPE_UNSPECIFIED": 0, "MEMO_COMMENT": 1, - "VERSION_UPDATE": 2, } ) @@ -513,7 +509,7 @@ var File_api_v1_activity_service_proto protoreflect.FileDescriptor const file_api_v1_activity_service_proto_rawDesc = "" + "\n" + - "\x1dapi/v1/activity_service.proto\x12\fmemos.api.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x86\x04\n" + + "\x1dapi/v1/activity_service.proto\x12\fmemos.api.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xf2\x03\n" + "\bActivity\x12\x1a\n" + "\x04name\x18\x01 \x01(\tB\x06\xe0A\x03\xe0A\bR\x04name\x12\x1d\n" + "\acreator\x18\x02 \x01(\tB\x03\xe0A\x03R\acreator\x124\n" + @@ -521,11 +517,10 @@ const file_api_v1_activity_service_proto_rawDesc = "" + "\x05level\x18\x04 \x01(\x0e2\x1c.memos.api.v1.Activity.LevelB\x03\xe0A\x03R\x05level\x12@\n" + "\vcreate_time\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampB\x03\xe0A\x03R\n" + "createTime\x12<\n" + - "\apayload\x18\x06 \x01(\v2\x1d.memos.api.v1.ActivityPayloadB\x03\xe0A\x03R\apayload\"B\n" + + "\apayload\x18\x06 \x01(\v2\x1d.memos.api.v1.ActivityPayloadB\x03\xe0A\x03R\apayload\".\n" + "\x04Type\x12\x14\n" + "\x10TYPE_UNSPECIFIED\x10\x00\x12\x10\n" + - "\fMEMO_COMMENT\x10\x01\x12\x12\n" + - "\x0eVERSION_UPDATE\x10\x02\"=\n" + + "\fMEMO_COMMENT\x10\x01\"=\n" + "\x05Level\x12\x15\n" + "\x11LEVEL_UNSPECIFIED\x10\x00\x12\b\n" + "\x04INFO\x10\x01\x12\b\n" + diff --git a/proto/gen/api/v1/inbox_service.pb.go b/proto/gen/api/v1/inbox_service.pb.go index 47a3d2246..fdcdc527c 100644 --- a/proto/gen/api/v1/inbox_service.pb.go +++ b/proto/gen/api/v1/inbox_service.pb.go @@ -86,8 +86,6 @@ const ( Inbox_TYPE_UNSPECIFIED Inbox_Type = 0 // Memo comment notification. Inbox_MEMO_COMMENT Inbox_Type = 1 - // Version update notification. - Inbox_VERSION_UPDATE Inbox_Type = 2 ) // Enum value maps for Inbox_Type. @@ -95,12 +93,10 @@ var ( Inbox_Type_name = map[int32]string{ 0: "TYPE_UNSPECIFIED", 1: "MEMO_COMMENT", - 2: "VERSION_UPDATE", } Inbox_Type_value = map[string]int32{ "TYPE_UNSPECIFIED": 0, "MEMO_COMMENT": 1, - "VERSION_UPDATE": 2, } ) @@ -500,7 +496,7 @@ var File_api_v1_inbox_service_proto protoreflect.FileDescriptor const file_api_v1_inbox_service_proto_rawDesc = "" + "\n" + - "\x1aapi/v1/inbox_service.proto\x12\fmemos.api.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x87\x04\n" + + "\x1aapi/v1/inbox_service.proto\x12\fmemos.api.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xf3\x03\n" + "\x05Inbox\x12\x17\n" + "\x04name\x18\x01 \x01(\tB\x03\xe0A\bR\x04name\x12\x1b\n" + "\x06sender\x18\x02 \x01(\tB\x03\xe0A\x03R\x06sender\x12\x1f\n" + @@ -515,11 +511,10 @@ const file_api_v1_inbox_service_proto_rawDesc = "" + "\x12STATUS_UNSPECIFIED\x10\x00\x12\n" + "\n" + "\x06UNREAD\x10\x01\x12\f\n" + - "\bARCHIVED\x10\x02\"B\n" + + "\bARCHIVED\x10\x02\".\n" + "\x04Type\x12\x14\n" + "\x10TYPE_UNSPECIFIED\x10\x00\x12\x10\n" + - "\fMEMO_COMMENT\x10\x01\x12\x12\n" + - "\x0eVERSION_UPDATE\x10\x02:>\xeaA;\n" + + "\fMEMO_COMMENT\x10\x01:>\xeaA;\n" + "\x12memos.api.v1/Inbox\x12\x0finboxes/{inbox}\x1a\x04name*\ainboxes2\x05inboxB\x0e\n" + "\f_activity_id\"\xca\x01\n" + "\x12ListInboxesRequest\x121\n" + diff --git a/proto/gen/api/v1/user_service.pb.go b/proto/gen/api/v1/user_service.pb.go index 2067416b6..7b62f212a 100644 --- a/proto/gen/api/v1/user_service.pb.go +++ b/proto/gen/api/v1/user_service.pb.go @@ -143,6 +143,101 @@ func (UserSetting_Key) EnumDescriptor() ([]byte, []int) { return file_api_v1_user_service_proto_rawDescGZIP(), []int{12, 0} } +type UserNotification_Status int32 + +const ( + UserNotification_STATUS_UNSPECIFIED UserNotification_Status = 0 + UserNotification_UNREAD UserNotification_Status = 1 + UserNotification_ARCHIVED UserNotification_Status = 2 +) + +// Enum value maps for UserNotification_Status. +var ( + UserNotification_Status_name = map[int32]string{ + 0: "STATUS_UNSPECIFIED", + 1: "UNREAD", + 2: "ARCHIVED", + } + UserNotification_Status_value = map[string]int32{ + "STATUS_UNSPECIFIED": 0, + "UNREAD": 1, + "ARCHIVED": 2, + } +) + +func (x UserNotification_Status) Enum() *UserNotification_Status { + p := new(UserNotification_Status) + *p = x + return p +} + +func (x UserNotification_Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (UserNotification_Status) Descriptor() protoreflect.EnumDescriptor { + return file_api_v1_user_service_proto_enumTypes[2].Descriptor() +} + +func (UserNotification_Status) Type() protoreflect.EnumType { + return &file_api_v1_user_service_proto_enumTypes[2] +} + +func (x UserNotification_Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use UserNotification_Status.Descriptor instead. +func (UserNotification_Status) EnumDescriptor() ([]byte, []int) { + return file_api_v1_user_service_proto_rawDescGZIP(), []int{32, 0} +} + +type UserNotification_Type int32 + +const ( + UserNotification_TYPE_UNSPECIFIED UserNotification_Type = 0 + UserNotification_MEMO_COMMENT UserNotification_Type = 1 +) + +// Enum value maps for UserNotification_Type. +var ( + UserNotification_Type_name = map[int32]string{ + 0: "TYPE_UNSPECIFIED", + 1: "MEMO_COMMENT", + } + UserNotification_Type_value = map[string]int32{ + "TYPE_UNSPECIFIED": 0, + "MEMO_COMMENT": 1, + } +) + +func (x UserNotification_Type) Enum() *UserNotification_Type { + p := new(UserNotification_Type) + *p = x + return p +} + +func (x UserNotification_Type) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (UserNotification_Type) Descriptor() protoreflect.EnumDescriptor { + return file_api_v1_user_service_proto_enumTypes[3].Descriptor() +} + +func (UserNotification_Type) Type() protoreflect.EnumType { + return &file_api_v1_user_service_proto_enumTypes[3] +} + +func (x UserNotification_Type) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use UserNotification_Type.Descriptor instead. +func (UserNotification_Type) EnumDescriptor() ([]byte, []int) { + return file_api_v1_user_service_proto_rawDescGZIP(), []int{32, 1} +} + type User struct { state protoimpl.MessageState `protogen:"open.v1"` // The resource name of the user. @@ -2169,6 +2264,317 @@ func (x *DeleteUserWebhookRequest) GetName() string { return "" } +type UserNotification struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The resource name of the notification. + // Format: users/{user}/notifications/{notification} + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // The sender of the notification. + // Format: users/{user} + Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty"` + // The status of the notification. + Status UserNotification_Status `protobuf:"varint,3,opt,name=status,proto3,enum=memos.api.v1.UserNotification_Status" json:"status,omitempty"` + // The creation timestamp. + CreateTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty"` + // The type of the notification. + Type UserNotification_Type `protobuf:"varint,5,opt,name=type,proto3,enum=memos.api.v1.UserNotification_Type" json:"type,omitempty"` + // The activity ID associated with this notification. + ActivityId *int32 `protobuf:"varint,6,opt,name=activity_id,json=activityId,proto3,oneof" json:"activity_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UserNotification) Reset() { + *x = UserNotification{} + mi := &file_api_v1_user_service_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UserNotification) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UserNotification) ProtoMessage() {} + +func (x *UserNotification) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_user_service_proto_msgTypes[32] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UserNotification.ProtoReflect.Descriptor instead. +func (*UserNotification) Descriptor() ([]byte, []int) { + return file_api_v1_user_service_proto_rawDescGZIP(), []int{32} +} + +func (x *UserNotification) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *UserNotification) GetSender() string { + if x != nil { + return x.Sender + } + return "" +} + +func (x *UserNotification) GetStatus() UserNotification_Status { + if x != nil { + return x.Status + } + return UserNotification_STATUS_UNSPECIFIED +} + +func (x *UserNotification) GetCreateTime() *timestamppb.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + +func (x *UserNotification) GetType() UserNotification_Type { + if x != nil { + return x.Type + } + return UserNotification_TYPE_UNSPECIFIED +} + +func (x *UserNotification) GetActivityId() int32 { + if x != nil && x.ActivityId != nil { + return *x.ActivityId + } + return 0 +} + +type ListUserNotificationsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The parent user resource. + // Format: users/{user} + Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` + PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` + Filter string `protobuf:"bytes,4,opt,name=filter,proto3" json:"filter,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListUserNotificationsRequest) Reset() { + *x = ListUserNotificationsRequest{} + mi := &file_api_v1_user_service_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListUserNotificationsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListUserNotificationsRequest) ProtoMessage() {} + +func (x *ListUserNotificationsRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_user_service_proto_msgTypes[33] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListUserNotificationsRequest.ProtoReflect.Descriptor instead. +func (*ListUserNotificationsRequest) Descriptor() ([]byte, []int) { + return file_api_v1_user_service_proto_rawDescGZIP(), []int{33} +} + +func (x *ListUserNotificationsRequest) GetParent() string { + if x != nil { + return x.Parent + } + return "" +} + +func (x *ListUserNotificationsRequest) GetPageSize() int32 { + if x != nil { + return x.PageSize + } + return 0 +} + +func (x *ListUserNotificationsRequest) GetPageToken() string { + if x != nil { + return x.PageToken + } + return "" +} + +func (x *ListUserNotificationsRequest) GetFilter() string { + if x != nil { + return x.Filter + } + return "" +} + +type ListUserNotificationsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Notifications []*UserNotification `protobuf:"bytes,1,rep,name=notifications,proto3" json:"notifications,omitempty"` + NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListUserNotificationsResponse) Reset() { + *x = ListUserNotificationsResponse{} + mi := &file_api_v1_user_service_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListUserNotificationsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListUserNotificationsResponse) ProtoMessage() {} + +func (x *ListUserNotificationsResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_user_service_proto_msgTypes[34] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListUserNotificationsResponse.ProtoReflect.Descriptor instead. +func (*ListUserNotificationsResponse) Descriptor() ([]byte, []int) { + return file_api_v1_user_service_proto_rawDescGZIP(), []int{34} +} + +func (x *ListUserNotificationsResponse) GetNotifications() []*UserNotification { + if x != nil { + return x.Notifications + } + return nil +} + +func (x *ListUserNotificationsResponse) GetNextPageToken() string { + if x != nil { + return x.NextPageToken + } + return "" +} + +type UpdateUserNotificationRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Notification *UserNotification `protobuf:"bytes,1,opt,name=notification,proto3" json:"notification,omitempty"` + UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateUserNotificationRequest) Reset() { + *x = UpdateUserNotificationRequest{} + mi := &file_api_v1_user_service_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateUserNotificationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateUserNotificationRequest) ProtoMessage() {} + +func (x *UpdateUserNotificationRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_user_service_proto_msgTypes[35] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateUserNotificationRequest.ProtoReflect.Descriptor instead. +func (*UpdateUserNotificationRequest) Descriptor() ([]byte, []int) { + return file_api_v1_user_service_proto_rawDescGZIP(), []int{35} +} + +func (x *UpdateUserNotificationRequest) GetNotification() *UserNotification { + if x != nil { + return x.Notification + } + return nil +} + +func (x *UpdateUserNotificationRequest) GetUpdateMask() *fieldmaskpb.FieldMask { + if x != nil { + return x.UpdateMask + } + return nil +} + +type DeleteUserNotificationRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Format: users/{user}/notifications/{notification} + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteUserNotificationRequest) Reset() { + *x = DeleteUserNotificationRequest{} + mi := &file_api_v1_user_service_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteUserNotificationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteUserNotificationRequest) ProtoMessage() {} + +func (x *DeleteUserNotificationRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_user_service_proto_msgTypes[36] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteUserNotificationRequest.ProtoReflect.Descriptor instead. +func (*DeleteUserNotificationRequest) Descriptor() ([]byte, []int) { + return file_api_v1_user_service_proto_rawDescGZIP(), []int{36} +} + +func (x *DeleteUserNotificationRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + // Memo type statistics. type UserStats_MemoTypeStats struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -2182,7 +2588,7 @@ type UserStats_MemoTypeStats struct { func (x *UserStats_MemoTypeStats) Reset() { *x = UserStats_MemoTypeStats{} - mi := &file_api_v1_user_service_proto_msgTypes[33] + mi := &file_api_v1_user_service_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2194,7 +2600,7 @@ func (x *UserStats_MemoTypeStats) String() string { func (*UserStats_MemoTypeStats) ProtoMessage() {} func (x *UserStats_MemoTypeStats) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_user_service_proto_msgTypes[33] + mi := &file_api_v1_user_service_proto_msgTypes[38] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2255,7 +2661,7 @@ type UserSetting_GeneralSetting struct { func (x *UserSetting_GeneralSetting) Reset() { *x = UserSetting_GeneralSetting{} - mi := &file_api_v1_user_service_proto_msgTypes[34] + mi := &file_api_v1_user_service_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2267,7 +2673,7 @@ func (x *UserSetting_GeneralSetting) String() string { func (*UserSetting_GeneralSetting) ProtoMessage() {} func (x *UserSetting_GeneralSetting) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_user_service_proto_msgTypes[34] + mi := &file_api_v1_user_service_proto_msgTypes[39] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2315,7 +2721,7 @@ type UserSetting_SessionsSetting struct { func (x *UserSetting_SessionsSetting) Reset() { *x = UserSetting_SessionsSetting{} - mi := &file_api_v1_user_service_proto_msgTypes[35] + mi := &file_api_v1_user_service_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2327,7 +2733,7 @@ func (x *UserSetting_SessionsSetting) String() string { func (*UserSetting_SessionsSetting) ProtoMessage() {} func (x *UserSetting_SessionsSetting) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_user_service_proto_msgTypes[35] + mi := &file_api_v1_user_service_proto_msgTypes[40] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2361,7 +2767,7 @@ type UserSetting_AccessTokensSetting struct { func (x *UserSetting_AccessTokensSetting) Reset() { *x = UserSetting_AccessTokensSetting{} - mi := &file_api_v1_user_service_proto_msgTypes[36] + mi := &file_api_v1_user_service_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2373,7 +2779,7 @@ func (x *UserSetting_AccessTokensSetting) String() string { func (*UserSetting_AccessTokensSetting) ProtoMessage() {} func (x *UserSetting_AccessTokensSetting) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_user_service_proto_msgTypes[36] + mi := &file_api_v1_user_service_proto_msgTypes[41] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2407,7 +2813,7 @@ type UserSetting_WebhooksSetting struct { func (x *UserSetting_WebhooksSetting) Reset() { *x = UserSetting_WebhooksSetting{} - mi := &file_api_v1_user_service_proto_msgTypes[37] + mi := &file_api_v1_user_service_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2419,7 +2825,7 @@ func (x *UserSetting_WebhooksSetting) String() string { func (*UserSetting_WebhooksSetting) ProtoMessage() {} func (x *UserSetting_WebhooksSetting) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_user_service_proto_msgTypes[37] + mi := &file_api_v1_user_service_proto_msgTypes[42] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2460,7 +2866,7 @@ type UserSession_ClientInfo struct { func (x *UserSession_ClientInfo) Reset() { *x = UserSession_ClientInfo{} - mi := &file_api_v1_user_service_proto_msgTypes[38] + mi := &file_api_v1_user_service_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2472,7 +2878,7 @@ func (x *UserSession_ClientInfo) String() string { func (*UserSession_ClientInfo) ProtoMessage() {} func (x *UserSession_ClientInfo) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_user_service_proto_msgTypes[38] + mi := &file_api_v1_user_service_proto_msgTypes[43] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2725,7 +3131,44 @@ const file_api_v1_user_service_proto_rawDesc = "" + "\vupdate_mask\x18\x02 \x01(\v2\x1a.google.protobuf.FieldMaskR\n" + "updateMask\"3\n" + "\x18DeleteUserWebhookRequest\x12\x17\n" + - "\x04name\x18\x01 \x01(\tB\x03\xe0A\x02R\x04name2\xe6\x15\n" + + "\x04name\x18\x01 \x01(\tB\x03\xe0A\x02R\x04name\"\xbe\x04\n" + + "\x10UserNotification\x12\x1a\n" + + "\x04name\x18\x01 \x01(\tB\x06\xe0A\x03\xe0A\bR\x04name\x121\n" + + "\x06sender\x18\x02 \x01(\tB\x19\xe0A\x03\xfaA\x13\n" + + "\x11memos.api.v1/UserR\x06sender\x12B\n" + + "\x06status\x18\x03 \x01(\x0e2%.memos.api.v1.UserNotification.StatusB\x03\xe0A\x01R\x06status\x12@\n" + + "\vcreate_time\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampB\x03\xe0A\x03R\n" + + "createTime\x12<\n" + + "\x04type\x18\x05 \x01(\x0e2#.memos.api.v1.UserNotification.TypeB\x03\xe0A\x03R\x04type\x12)\n" + + "\vactivity_id\x18\x06 \x01(\x05B\x03\xe0A\x01H\x00R\n" + + "activityId\x88\x01\x01\":\n" + + "\x06Status\x12\x16\n" + + "\x12STATUS_UNSPECIFIED\x10\x00\x12\n" + + "\n" + + "\x06UNREAD\x10\x01\x12\f\n" + + "\bARCHIVED\x10\x02\".\n" + + "\x04Type\x12\x14\n" + + "\x10TYPE_UNSPECIFIED\x10\x00\x12\x10\n" + + "\fMEMO_COMMENT\x10\x01:p\xeaAm\n" + + "\x1dmemos.api.v1/UserNotification\x12)users/{user}/notifications/{notification}\x1a\x04name*\rnotifications2\fnotificationB\x0e\n" + + "\f_activity_id\"\xb4\x01\n" + + "\x1cListUserNotificationsRequest\x121\n" + + "\x06parent\x18\x01 \x01(\tB\x19\xe0A\x02\xfaA\x13\n" + + "\x11memos.api.v1/UserR\x06parent\x12 \n" + + "\tpage_size\x18\x02 \x01(\x05B\x03\xe0A\x01R\bpageSize\x12\"\n" + + "\n" + + "page_token\x18\x03 \x01(\tB\x03\xe0A\x01R\tpageToken\x12\x1b\n" + + "\x06filter\x18\x04 \x01(\tB\x03\xe0A\x01R\x06filter\"\x8d\x01\n" + + "\x1dListUserNotificationsResponse\x12D\n" + + "\rnotifications\x18\x01 \x03(\v2\x1e.memos.api.v1.UserNotificationR\rnotifications\x12&\n" + + "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"\xaa\x01\n" + + "\x1dUpdateUserNotificationRequest\x12G\n" + + "\fnotification\x18\x01 \x01(\v2\x1e.memos.api.v1.UserNotificationB\x03\xe0A\x02R\fnotification\x12@\n" + + "\vupdate_mask\x18\x02 \x01(\v2\x1a.google.protobuf.FieldMaskB\x03\xe0A\x02R\n" + + "updateMask\"Z\n" + + "\x1dDeleteUserNotificationRequest\x129\n" + + "\x04name\x18\x01 \x01(\tB%\xe0A\x02\xfaA\x1f\n" + + "\x1dmemos.api.v1/UserNotificationR\x04name2\xf7\x19\n" + "\vUserService\x12c\n" + "\tListUsers\x12\x1e.memos.api.v1.ListUsersRequest\x1a\x1f.memos.api.v1.ListUsersResponse\"\x15\x82\xd3\xe4\x93\x02\x0f\x12\r/api/v1/users\x12b\n" + "\aGetUser\x12\x1c.memos.api.v1.GetUserRequest\x1a\x12.memos.api.v1.User\"%\xdaA\x04name\x82\xd3\xe4\x93\x02\x18\x12\x16/api/v1/{name=users/*}\x12e\n" + @@ -2749,7 +3192,10 @@ const file_api_v1_user_service_proto_rawDesc = "" + "\x10ListUserWebhooks\x12%.memos.api.v1.ListUserWebhooksRequest\x1a&.memos.api.v1.ListUserWebhooksResponse\"2\xdaA\x06parent\x82\xd3\xe4\x93\x02#\x12!/api/v1/{parent=users/*}/webhooks\x12\x9b\x01\n" + "\x11CreateUserWebhook\x12&.memos.api.v1.CreateUserWebhookRequest\x1a\x19.memos.api.v1.UserWebhook\"C\xdaA\x0eparent,webhook\x82\xd3\xe4\x93\x02,:\awebhook\"!/api/v1/{parent=users/*}/webhooks\x12\xa8\x01\n" + "\x11UpdateUserWebhook\x12&.memos.api.v1.UpdateUserWebhookRequest\x1a\x19.memos.api.v1.UserWebhook\"P\xdaA\x13webhook,update_mask\x82\xd3\xe4\x93\x024:\awebhook2)/api/v1/{webhook.name=users/*/webhooks/*}\x12\x85\x01\n" + - "\x11DeleteUserWebhook\x12&.memos.api.v1.DeleteUserWebhookRequest\x1a\x16.google.protobuf.Empty\"0\xdaA\x04name\x82\xd3\xe4\x93\x02#*!/api/v1/{name=users/*/webhooks/*}B\xa8\x01\n" + + "\x11DeleteUserWebhook\x12&.memos.api.v1.DeleteUserWebhookRequest\x1a\x16.google.protobuf.Empty\"0\xdaA\x04name\x82\xd3\xe4\x93\x02#*!/api/v1/{name=users/*/webhooks/*}\x12\xa9\x01\n" + + "\x15ListUserNotifications\x12*.memos.api.v1.ListUserNotificationsRequest\x1a+.memos.api.v1.ListUserNotificationsResponse\"7\xdaA\x06parent\x82\xd3\xe4\x93\x02(\x12&/api/v1/{parent=users/*}/notifications\x12\xcb\x01\n" + + "\x16UpdateUserNotification\x12+.memos.api.v1.UpdateUserNotificationRequest\x1a\x1e.memos.api.v1.UserNotification\"d\xdaA\x18notification,update_mask\x82\xd3\xe4\x93\x02C:\fnotification23/api/v1/{notification.name=users/*/notifications/*}\x12\x94\x01\n" + + "\x16DeleteUserNotification\x12+.memos.api.v1.DeleteUserNotificationRequest\x1a\x16.google.protobuf.Empty\"5\xdaA\x04name\x82\xd3\xe4\x93\x02(*&/api/v1/{name=users/*/notifications/*}B\xa8\x01\n" + "\x10com.memos.api.v1B\x10UserServiceProtoP\x01Z0github.com/usememos/memos/proto/gen/api/v1;apiv1\xa2\x02\x03MAX\xaa\x02\fMemos.Api.V1\xca\x02\fMemos\\Api\\V1\xe2\x02\x18Memos\\Api\\V1\\GPBMetadata\xea\x02\x0eMemos::Api::V1b\x06proto3" var ( @@ -2764,139 +3210,158 @@ func file_api_v1_user_service_proto_rawDescGZIP() []byte { return file_api_v1_user_service_proto_rawDescData } -var file_api_v1_user_service_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_api_v1_user_service_proto_msgTypes = make([]protoimpl.MessageInfo, 39) +var file_api_v1_user_service_proto_enumTypes = make([]protoimpl.EnumInfo, 4) +var file_api_v1_user_service_proto_msgTypes = make([]protoimpl.MessageInfo, 44) var file_api_v1_user_service_proto_goTypes = []any{ (User_Role)(0), // 0: memos.api.v1.User.Role (UserSetting_Key)(0), // 1: memos.api.v1.UserSetting.Key - (*User)(nil), // 2: memos.api.v1.User - (*ListUsersRequest)(nil), // 3: memos.api.v1.ListUsersRequest - (*ListUsersResponse)(nil), // 4: memos.api.v1.ListUsersResponse - (*GetUserRequest)(nil), // 5: memos.api.v1.GetUserRequest - (*CreateUserRequest)(nil), // 6: memos.api.v1.CreateUserRequest - (*UpdateUserRequest)(nil), // 7: memos.api.v1.UpdateUserRequest - (*DeleteUserRequest)(nil), // 8: memos.api.v1.DeleteUserRequest - (*GetUserAvatarRequest)(nil), // 9: memos.api.v1.GetUserAvatarRequest - (*UserStats)(nil), // 10: memos.api.v1.UserStats - (*GetUserStatsRequest)(nil), // 11: memos.api.v1.GetUserStatsRequest - (*ListAllUserStatsRequest)(nil), // 12: memos.api.v1.ListAllUserStatsRequest - (*ListAllUserStatsResponse)(nil), // 13: memos.api.v1.ListAllUserStatsResponse - (*UserSetting)(nil), // 14: memos.api.v1.UserSetting - (*GetUserSettingRequest)(nil), // 15: memos.api.v1.GetUserSettingRequest - (*UpdateUserSettingRequest)(nil), // 16: memos.api.v1.UpdateUserSettingRequest - (*ListUserSettingsRequest)(nil), // 17: memos.api.v1.ListUserSettingsRequest - (*ListUserSettingsResponse)(nil), // 18: memos.api.v1.ListUserSettingsResponse - (*UserAccessToken)(nil), // 19: memos.api.v1.UserAccessToken - (*ListUserAccessTokensRequest)(nil), // 20: memos.api.v1.ListUserAccessTokensRequest - (*ListUserAccessTokensResponse)(nil), // 21: memos.api.v1.ListUserAccessTokensResponse - (*CreateUserAccessTokenRequest)(nil), // 22: memos.api.v1.CreateUserAccessTokenRequest - (*DeleteUserAccessTokenRequest)(nil), // 23: memos.api.v1.DeleteUserAccessTokenRequest - (*UserSession)(nil), // 24: memos.api.v1.UserSession - (*ListUserSessionsRequest)(nil), // 25: memos.api.v1.ListUserSessionsRequest - (*ListUserSessionsResponse)(nil), // 26: memos.api.v1.ListUserSessionsResponse - (*RevokeUserSessionRequest)(nil), // 27: memos.api.v1.RevokeUserSessionRequest - (*UserWebhook)(nil), // 28: memos.api.v1.UserWebhook - (*ListUserWebhooksRequest)(nil), // 29: memos.api.v1.ListUserWebhooksRequest - (*ListUserWebhooksResponse)(nil), // 30: memos.api.v1.ListUserWebhooksResponse - (*CreateUserWebhookRequest)(nil), // 31: memos.api.v1.CreateUserWebhookRequest - (*UpdateUserWebhookRequest)(nil), // 32: memos.api.v1.UpdateUserWebhookRequest - (*DeleteUserWebhookRequest)(nil), // 33: memos.api.v1.DeleteUserWebhookRequest - nil, // 34: memos.api.v1.UserStats.TagCountEntry - (*UserStats_MemoTypeStats)(nil), // 35: memos.api.v1.UserStats.MemoTypeStats - (*UserSetting_GeneralSetting)(nil), // 36: memos.api.v1.UserSetting.GeneralSetting - (*UserSetting_SessionsSetting)(nil), // 37: memos.api.v1.UserSetting.SessionsSetting - (*UserSetting_AccessTokensSetting)(nil), // 38: memos.api.v1.UserSetting.AccessTokensSetting - (*UserSetting_WebhooksSetting)(nil), // 39: memos.api.v1.UserSetting.WebhooksSetting - (*UserSession_ClientInfo)(nil), // 40: memos.api.v1.UserSession.ClientInfo - (State)(0), // 41: memos.api.v1.State - (*timestamppb.Timestamp)(nil), // 42: google.protobuf.Timestamp - (*fieldmaskpb.FieldMask)(nil), // 43: google.protobuf.FieldMask - (*emptypb.Empty)(nil), // 44: google.protobuf.Empty - (*httpbody.HttpBody)(nil), // 45: google.api.HttpBody + (UserNotification_Status)(0), // 2: memos.api.v1.UserNotification.Status + (UserNotification_Type)(0), // 3: memos.api.v1.UserNotification.Type + (*User)(nil), // 4: memos.api.v1.User + (*ListUsersRequest)(nil), // 5: memos.api.v1.ListUsersRequest + (*ListUsersResponse)(nil), // 6: memos.api.v1.ListUsersResponse + (*GetUserRequest)(nil), // 7: memos.api.v1.GetUserRequest + (*CreateUserRequest)(nil), // 8: memos.api.v1.CreateUserRequest + (*UpdateUserRequest)(nil), // 9: memos.api.v1.UpdateUserRequest + (*DeleteUserRequest)(nil), // 10: memos.api.v1.DeleteUserRequest + (*GetUserAvatarRequest)(nil), // 11: memos.api.v1.GetUserAvatarRequest + (*UserStats)(nil), // 12: memos.api.v1.UserStats + (*GetUserStatsRequest)(nil), // 13: memos.api.v1.GetUserStatsRequest + (*ListAllUserStatsRequest)(nil), // 14: memos.api.v1.ListAllUserStatsRequest + (*ListAllUserStatsResponse)(nil), // 15: memos.api.v1.ListAllUserStatsResponse + (*UserSetting)(nil), // 16: memos.api.v1.UserSetting + (*GetUserSettingRequest)(nil), // 17: memos.api.v1.GetUserSettingRequest + (*UpdateUserSettingRequest)(nil), // 18: memos.api.v1.UpdateUserSettingRequest + (*ListUserSettingsRequest)(nil), // 19: memos.api.v1.ListUserSettingsRequest + (*ListUserSettingsResponse)(nil), // 20: memos.api.v1.ListUserSettingsResponse + (*UserAccessToken)(nil), // 21: memos.api.v1.UserAccessToken + (*ListUserAccessTokensRequest)(nil), // 22: memos.api.v1.ListUserAccessTokensRequest + (*ListUserAccessTokensResponse)(nil), // 23: memos.api.v1.ListUserAccessTokensResponse + (*CreateUserAccessTokenRequest)(nil), // 24: memos.api.v1.CreateUserAccessTokenRequest + (*DeleteUserAccessTokenRequest)(nil), // 25: memos.api.v1.DeleteUserAccessTokenRequest + (*UserSession)(nil), // 26: memos.api.v1.UserSession + (*ListUserSessionsRequest)(nil), // 27: memos.api.v1.ListUserSessionsRequest + (*ListUserSessionsResponse)(nil), // 28: memos.api.v1.ListUserSessionsResponse + (*RevokeUserSessionRequest)(nil), // 29: memos.api.v1.RevokeUserSessionRequest + (*UserWebhook)(nil), // 30: memos.api.v1.UserWebhook + (*ListUserWebhooksRequest)(nil), // 31: memos.api.v1.ListUserWebhooksRequest + (*ListUserWebhooksResponse)(nil), // 32: memos.api.v1.ListUserWebhooksResponse + (*CreateUserWebhookRequest)(nil), // 33: memos.api.v1.CreateUserWebhookRequest + (*UpdateUserWebhookRequest)(nil), // 34: memos.api.v1.UpdateUserWebhookRequest + (*DeleteUserWebhookRequest)(nil), // 35: memos.api.v1.DeleteUserWebhookRequest + (*UserNotification)(nil), // 36: memos.api.v1.UserNotification + (*ListUserNotificationsRequest)(nil), // 37: memos.api.v1.ListUserNotificationsRequest + (*ListUserNotificationsResponse)(nil), // 38: memos.api.v1.ListUserNotificationsResponse + (*UpdateUserNotificationRequest)(nil), // 39: memos.api.v1.UpdateUserNotificationRequest + (*DeleteUserNotificationRequest)(nil), // 40: memos.api.v1.DeleteUserNotificationRequest + nil, // 41: memos.api.v1.UserStats.TagCountEntry + (*UserStats_MemoTypeStats)(nil), // 42: memos.api.v1.UserStats.MemoTypeStats + (*UserSetting_GeneralSetting)(nil), // 43: memos.api.v1.UserSetting.GeneralSetting + (*UserSetting_SessionsSetting)(nil), // 44: memos.api.v1.UserSetting.SessionsSetting + (*UserSetting_AccessTokensSetting)(nil), // 45: memos.api.v1.UserSetting.AccessTokensSetting + (*UserSetting_WebhooksSetting)(nil), // 46: memos.api.v1.UserSetting.WebhooksSetting + (*UserSession_ClientInfo)(nil), // 47: memos.api.v1.UserSession.ClientInfo + (State)(0), // 48: memos.api.v1.State + (*timestamppb.Timestamp)(nil), // 49: google.protobuf.Timestamp + (*fieldmaskpb.FieldMask)(nil), // 50: google.protobuf.FieldMask + (*emptypb.Empty)(nil), // 51: google.protobuf.Empty + (*httpbody.HttpBody)(nil), // 52: google.api.HttpBody } var file_api_v1_user_service_proto_depIdxs = []int32{ 0, // 0: memos.api.v1.User.role:type_name -> memos.api.v1.User.Role - 41, // 1: memos.api.v1.User.state:type_name -> memos.api.v1.State - 42, // 2: memos.api.v1.User.create_time:type_name -> google.protobuf.Timestamp - 42, // 3: memos.api.v1.User.update_time:type_name -> google.protobuf.Timestamp - 2, // 4: memos.api.v1.ListUsersResponse.users:type_name -> memos.api.v1.User - 43, // 5: memos.api.v1.GetUserRequest.read_mask:type_name -> google.protobuf.FieldMask - 2, // 6: memos.api.v1.CreateUserRequest.user:type_name -> memos.api.v1.User - 2, // 7: memos.api.v1.UpdateUserRequest.user:type_name -> memos.api.v1.User - 43, // 8: memos.api.v1.UpdateUserRequest.update_mask:type_name -> google.protobuf.FieldMask - 42, // 9: memos.api.v1.UserStats.memo_display_timestamps:type_name -> google.protobuf.Timestamp - 35, // 10: memos.api.v1.UserStats.memo_type_stats:type_name -> memos.api.v1.UserStats.MemoTypeStats - 34, // 11: memos.api.v1.UserStats.tag_count:type_name -> memos.api.v1.UserStats.TagCountEntry - 10, // 12: memos.api.v1.ListAllUserStatsResponse.stats:type_name -> memos.api.v1.UserStats - 36, // 13: memos.api.v1.UserSetting.general_setting:type_name -> memos.api.v1.UserSetting.GeneralSetting - 37, // 14: memos.api.v1.UserSetting.sessions_setting:type_name -> memos.api.v1.UserSetting.SessionsSetting - 38, // 15: memos.api.v1.UserSetting.access_tokens_setting:type_name -> memos.api.v1.UserSetting.AccessTokensSetting - 39, // 16: memos.api.v1.UserSetting.webhooks_setting:type_name -> memos.api.v1.UserSetting.WebhooksSetting - 14, // 17: memos.api.v1.UpdateUserSettingRequest.setting:type_name -> memos.api.v1.UserSetting - 43, // 18: memos.api.v1.UpdateUserSettingRequest.update_mask:type_name -> google.protobuf.FieldMask - 14, // 19: memos.api.v1.ListUserSettingsResponse.settings:type_name -> memos.api.v1.UserSetting - 42, // 20: memos.api.v1.UserAccessToken.issued_at:type_name -> google.protobuf.Timestamp - 42, // 21: memos.api.v1.UserAccessToken.expires_at:type_name -> google.protobuf.Timestamp - 19, // 22: memos.api.v1.ListUserAccessTokensResponse.access_tokens:type_name -> memos.api.v1.UserAccessToken - 19, // 23: memos.api.v1.CreateUserAccessTokenRequest.access_token:type_name -> memos.api.v1.UserAccessToken - 42, // 24: memos.api.v1.UserSession.create_time:type_name -> google.protobuf.Timestamp - 42, // 25: memos.api.v1.UserSession.last_accessed_time:type_name -> google.protobuf.Timestamp - 40, // 26: memos.api.v1.UserSession.client_info:type_name -> memos.api.v1.UserSession.ClientInfo - 24, // 27: memos.api.v1.ListUserSessionsResponse.sessions:type_name -> memos.api.v1.UserSession - 42, // 28: memos.api.v1.UserWebhook.create_time:type_name -> google.protobuf.Timestamp - 42, // 29: memos.api.v1.UserWebhook.update_time:type_name -> google.protobuf.Timestamp - 28, // 30: memos.api.v1.ListUserWebhooksResponse.webhooks:type_name -> memos.api.v1.UserWebhook - 28, // 31: memos.api.v1.CreateUserWebhookRequest.webhook:type_name -> memos.api.v1.UserWebhook - 28, // 32: memos.api.v1.UpdateUserWebhookRequest.webhook:type_name -> memos.api.v1.UserWebhook - 43, // 33: memos.api.v1.UpdateUserWebhookRequest.update_mask:type_name -> google.protobuf.FieldMask - 24, // 34: memos.api.v1.UserSetting.SessionsSetting.sessions:type_name -> memos.api.v1.UserSession - 19, // 35: memos.api.v1.UserSetting.AccessTokensSetting.access_tokens:type_name -> memos.api.v1.UserAccessToken - 28, // 36: memos.api.v1.UserSetting.WebhooksSetting.webhooks:type_name -> memos.api.v1.UserWebhook - 3, // 37: memos.api.v1.UserService.ListUsers:input_type -> memos.api.v1.ListUsersRequest - 5, // 38: memos.api.v1.UserService.GetUser:input_type -> memos.api.v1.GetUserRequest - 6, // 39: memos.api.v1.UserService.CreateUser:input_type -> memos.api.v1.CreateUserRequest - 7, // 40: memos.api.v1.UserService.UpdateUser:input_type -> memos.api.v1.UpdateUserRequest - 8, // 41: memos.api.v1.UserService.DeleteUser:input_type -> memos.api.v1.DeleteUserRequest - 9, // 42: memos.api.v1.UserService.GetUserAvatar:input_type -> memos.api.v1.GetUserAvatarRequest - 12, // 43: memos.api.v1.UserService.ListAllUserStats:input_type -> memos.api.v1.ListAllUserStatsRequest - 11, // 44: memos.api.v1.UserService.GetUserStats:input_type -> memos.api.v1.GetUserStatsRequest - 15, // 45: memos.api.v1.UserService.GetUserSetting:input_type -> memos.api.v1.GetUserSettingRequest - 16, // 46: memos.api.v1.UserService.UpdateUserSetting:input_type -> memos.api.v1.UpdateUserSettingRequest - 17, // 47: memos.api.v1.UserService.ListUserSettings:input_type -> memos.api.v1.ListUserSettingsRequest - 20, // 48: memos.api.v1.UserService.ListUserAccessTokens:input_type -> memos.api.v1.ListUserAccessTokensRequest - 22, // 49: memos.api.v1.UserService.CreateUserAccessToken:input_type -> memos.api.v1.CreateUserAccessTokenRequest - 23, // 50: memos.api.v1.UserService.DeleteUserAccessToken:input_type -> memos.api.v1.DeleteUserAccessTokenRequest - 25, // 51: memos.api.v1.UserService.ListUserSessions:input_type -> memos.api.v1.ListUserSessionsRequest - 27, // 52: memos.api.v1.UserService.RevokeUserSession:input_type -> memos.api.v1.RevokeUserSessionRequest - 29, // 53: memos.api.v1.UserService.ListUserWebhooks:input_type -> memos.api.v1.ListUserWebhooksRequest - 31, // 54: memos.api.v1.UserService.CreateUserWebhook:input_type -> memos.api.v1.CreateUserWebhookRequest - 32, // 55: memos.api.v1.UserService.UpdateUserWebhook:input_type -> memos.api.v1.UpdateUserWebhookRequest - 33, // 56: memos.api.v1.UserService.DeleteUserWebhook:input_type -> memos.api.v1.DeleteUserWebhookRequest - 4, // 57: memos.api.v1.UserService.ListUsers:output_type -> memos.api.v1.ListUsersResponse - 2, // 58: memos.api.v1.UserService.GetUser:output_type -> memos.api.v1.User - 2, // 59: memos.api.v1.UserService.CreateUser:output_type -> memos.api.v1.User - 2, // 60: memos.api.v1.UserService.UpdateUser:output_type -> memos.api.v1.User - 44, // 61: memos.api.v1.UserService.DeleteUser:output_type -> google.protobuf.Empty - 45, // 62: memos.api.v1.UserService.GetUserAvatar:output_type -> google.api.HttpBody - 13, // 63: memos.api.v1.UserService.ListAllUserStats:output_type -> memos.api.v1.ListAllUserStatsResponse - 10, // 64: memos.api.v1.UserService.GetUserStats:output_type -> memos.api.v1.UserStats - 14, // 65: memos.api.v1.UserService.GetUserSetting:output_type -> memos.api.v1.UserSetting - 14, // 66: memos.api.v1.UserService.UpdateUserSetting:output_type -> memos.api.v1.UserSetting - 18, // 67: memos.api.v1.UserService.ListUserSettings:output_type -> memos.api.v1.ListUserSettingsResponse - 21, // 68: memos.api.v1.UserService.ListUserAccessTokens:output_type -> memos.api.v1.ListUserAccessTokensResponse - 19, // 69: memos.api.v1.UserService.CreateUserAccessToken:output_type -> memos.api.v1.UserAccessToken - 44, // 70: memos.api.v1.UserService.DeleteUserAccessToken:output_type -> google.protobuf.Empty - 26, // 71: memos.api.v1.UserService.ListUserSessions:output_type -> memos.api.v1.ListUserSessionsResponse - 44, // 72: memos.api.v1.UserService.RevokeUserSession:output_type -> google.protobuf.Empty - 30, // 73: memos.api.v1.UserService.ListUserWebhooks:output_type -> memos.api.v1.ListUserWebhooksResponse - 28, // 74: memos.api.v1.UserService.CreateUserWebhook:output_type -> memos.api.v1.UserWebhook - 28, // 75: memos.api.v1.UserService.UpdateUserWebhook:output_type -> memos.api.v1.UserWebhook - 44, // 76: memos.api.v1.UserService.DeleteUserWebhook:output_type -> google.protobuf.Empty - 57, // [57:77] is the sub-list for method output_type - 37, // [37:57] is the sub-list for method input_type - 37, // [37:37] is the sub-list for extension type_name - 37, // [37:37] is the sub-list for extension extendee - 0, // [0:37] is the sub-list for field type_name + 48, // 1: memos.api.v1.User.state:type_name -> memos.api.v1.State + 49, // 2: memos.api.v1.User.create_time:type_name -> google.protobuf.Timestamp + 49, // 3: memos.api.v1.User.update_time:type_name -> google.protobuf.Timestamp + 4, // 4: memos.api.v1.ListUsersResponse.users:type_name -> memos.api.v1.User + 50, // 5: memos.api.v1.GetUserRequest.read_mask:type_name -> google.protobuf.FieldMask + 4, // 6: memos.api.v1.CreateUserRequest.user:type_name -> memos.api.v1.User + 4, // 7: memos.api.v1.UpdateUserRequest.user:type_name -> memos.api.v1.User + 50, // 8: memos.api.v1.UpdateUserRequest.update_mask:type_name -> google.protobuf.FieldMask + 49, // 9: memos.api.v1.UserStats.memo_display_timestamps:type_name -> google.protobuf.Timestamp + 42, // 10: memos.api.v1.UserStats.memo_type_stats:type_name -> memos.api.v1.UserStats.MemoTypeStats + 41, // 11: memos.api.v1.UserStats.tag_count:type_name -> memos.api.v1.UserStats.TagCountEntry + 12, // 12: memos.api.v1.ListAllUserStatsResponse.stats:type_name -> memos.api.v1.UserStats + 43, // 13: memos.api.v1.UserSetting.general_setting:type_name -> memos.api.v1.UserSetting.GeneralSetting + 44, // 14: memos.api.v1.UserSetting.sessions_setting:type_name -> memos.api.v1.UserSetting.SessionsSetting + 45, // 15: memos.api.v1.UserSetting.access_tokens_setting:type_name -> memos.api.v1.UserSetting.AccessTokensSetting + 46, // 16: memos.api.v1.UserSetting.webhooks_setting:type_name -> memos.api.v1.UserSetting.WebhooksSetting + 16, // 17: memos.api.v1.UpdateUserSettingRequest.setting:type_name -> memos.api.v1.UserSetting + 50, // 18: memos.api.v1.UpdateUserSettingRequest.update_mask:type_name -> google.protobuf.FieldMask + 16, // 19: memos.api.v1.ListUserSettingsResponse.settings:type_name -> memos.api.v1.UserSetting + 49, // 20: memos.api.v1.UserAccessToken.issued_at:type_name -> google.protobuf.Timestamp + 49, // 21: memos.api.v1.UserAccessToken.expires_at:type_name -> google.protobuf.Timestamp + 21, // 22: memos.api.v1.ListUserAccessTokensResponse.access_tokens:type_name -> memos.api.v1.UserAccessToken + 21, // 23: memos.api.v1.CreateUserAccessTokenRequest.access_token:type_name -> memos.api.v1.UserAccessToken + 49, // 24: memos.api.v1.UserSession.create_time:type_name -> google.protobuf.Timestamp + 49, // 25: memos.api.v1.UserSession.last_accessed_time:type_name -> google.protobuf.Timestamp + 47, // 26: memos.api.v1.UserSession.client_info:type_name -> memos.api.v1.UserSession.ClientInfo + 26, // 27: memos.api.v1.ListUserSessionsResponse.sessions:type_name -> memos.api.v1.UserSession + 49, // 28: memos.api.v1.UserWebhook.create_time:type_name -> google.protobuf.Timestamp + 49, // 29: memos.api.v1.UserWebhook.update_time:type_name -> google.protobuf.Timestamp + 30, // 30: memos.api.v1.ListUserWebhooksResponse.webhooks:type_name -> memos.api.v1.UserWebhook + 30, // 31: memos.api.v1.CreateUserWebhookRequest.webhook:type_name -> memos.api.v1.UserWebhook + 30, // 32: memos.api.v1.UpdateUserWebhookRequest.webhook:type_name -> memos.api.v1.UserWebhook + 50, // 33: memos.api.v1.UpdateUserWebhookRequest.update_mask:type_name -> google.protobuf.FieldMask + 2, // 34: memos.api.v1.UserNotification.status:type_name -> memos.api.v1.UserNotification.Status + 49, // 35: memos.api.v1.UserNotification.create_time:type_name -> google.protobuf.Timestamp + 3, // 36: memos.api.v1.UserNotification.type:type_name -> memos.api.v1.UserNotification.Type + 36, // 37: memos.api.v1.ListUserNotificationsResponse.notifications:type_name -> memos.api.v1.UserNotification + 36, // 38: memos.api.v1.UpdateUserNotificationRequest.notification:type_name -> memos.api.v1.UserNotification + 50, // 39: memos.api.v1.UpdateUserNotificationRequest.update_mask:type_name -> google.protobuf.FieldMask + 26, // 40: memos.api.v1.UserSetting.SessionsSetting.sessions:type_name -> memos.api.v1.UserSession + 21, // 41: memos.api.v1.UserSetting.AccessTokensSetting.access_tokens:type_name -> memos.api.v1.UserAccessToken + 30, // 42: memos.api.v1.UserSetting.WebhooksSetting.webhooks:type_name -> memos.api.v1.UserWebhook + 5, // 43: memos.api.v1.UserService.ListUsers:input_type -> memos.api.v1.ListUsersRequest + 7, // 44: memos.api.v1.UserService.GetUser:input_type -> memos.api.v1.GetUserRequest + 8, // 45: memos.api.v1.UserService.CreateUser:input_type -> memos.api.v1.CreateUserRequest + 9, // 46: memos.api.v1.UserService.UpdateUser:input_type -> memos.api.v1.UpdateUserRequest + 10, // 47: memos.api.v1.UserService.DeleteUser:input_type -> memos.api.v1.DeleteUserRequest + 11, // 48: memos.api.v1.UserService.GetUserAvatar:input_type -> memos.api.v1.GetUserAvatarRequest + 14, // 49: memos.api.v1.UserService.ListAllUserStats:input_type -> memos.api.v1.ListAllUserStatsRequest + 13, // 50: memos.api.v1.UserService.GetUserStats:input_type -> memos.api.v1.GetUserStatsRequest + 17, // 51: memos.api.v1.UserService.GetUserSetting:input_type -> memos.api.v1.GetUserSettingRequest + 18, // 52: memos.api.v1.UserService.UpdateUserSetting:input_type -> memos.api.v1.UpdateUserSettingRequest + 19, // 53: memos.api.v1.UserService.ListUserSettings:input_type -> memos.api.v1.ListUserSettingsRequest + 22, // 54: memos.api.v1.UserService.ListUserAccessTokens:input_type -> memos.api.v1.ListUserAccessTokensRequest + 24, // 55: memos.api.v1.UserService.CreateUserAccessToken:input_type -> memos.api.v1.CreateUserAccessTokenRequest + 25, // 56: memos.api.v1.UserService.DeleteUserAccessToken:input_type -> memos.api.v1.DeleteUserAccessTokenRequest + 27, // 57: memos.api.v1.UserService.ListUserSessions:input_type -> memos.api.v1.ListUserSessionsRequest + 29, // 58: memos.api.v1.UserService.RevokeUserSession:input_type -> memos.api.v1.RevokeUserSessionRequest + 31, // 59: memos.api.v1.UserService.ListUserWebhooks:input_type -> memos.api.v1.ListUserWebhooksRequest + 33, // 60: memos.api.v1.UserService.CreateUserWebhook:input_type -> memos.api.v1.CreateUserWebhookRequest + 34, // 61: memos.api.v1.UserService.UpdateUserWebhook:input_type -> memos.api.v1.UpdateUserWebhookRequest + 35, // 62: memos.api.v1.UserService.DeleteUserWebhook:input_type -> memos.api.v1.DeleteUserWebhookRequest + 37, // 63: memos.api.v1.UserService.ListUserNotifications:input_type -> memos.api.v1.ListUserNotificationsRequest + 39, // 64: memos.api.v1.UserService.UpdateUserNotification:input_type -> memos.api.v1.UpdateUserNotificationRequest + 40, // 65: memos.api.v1.UserService.DeleteUserNotification:input_type -> memos.api.v1.DeleteUserNotificationRequest + 6, // 66: memos.api.v1.UserService.ListUsers:output_type -> memos.api.v1.ListUsersResponse + 4, // 67: memos.api.v1.UserService.GetUser:output_type -> memos.api.v1.User + 4, // 68: memos.api.v1.UserService.CreateUser:output_type -> memos.api.v1.User + 4, // 69: memos.api.v1.UserService.UpdateUser:output_type -> memos.api.v1.User + 51, // 70: memos.api.v1.UserService.DeleteUser:output_type -> google.protobuf.Empty + 52, // 71: memos.api.v1.UserService.GetUserAvatar:output_type -> google.api.HttpBody + 15, // 72: memos.api.v1.UserService.ListAllUserStats:output_type -> memos.api.v1.ListAllUserStatsResponse + 12, // 73: memos.api.v1.UserService.GetUserStats:output_type -> memos.api.v1.UserStats + 16, // 74: memos.api.v1.UserService.GetUserSetting:output_type -> memos.api.v1.UserSetting + 16, // 75: memos.api.v1.UserService.UpdateUserSetting:output_type -> memos.api.v1.UserSetting + 20, // 76: memos.api.v1.UserService.ListUserSettings:output_type -> memos.api.v1.ListUserSettingsResponse + 23, // 77: memos.api.v1.UserService.ListUserAccessTokens:output_type -> memos.api.v1.ListUserAccessTokensResponse + 21, // 78: memos.api.v1.UserService.CreateUserAccessToken:output_type -> memos.api.v1.UserAccessToken + 51, // 79: memos.api.v1.UserService.DeleteUserAccessToken:output_type -> google.protobuf.Empty + 28, // 80: memos.api.v1.UserService.ListUserSessions:output_type -> memos.api.v1.ListUserSessionsResponse + 51, // 81: memos.api.v1.UserService.RevokeUserSession:output_type -> google.protobuf.Empty + 32, // 82: memos.api.v1.UserService.ListUserWebhooks:output_type -> memos.api.v1.ListUserWebhooksResponse + 30, // 83: memos.api.v1.UserService.CreateUserWebhook:output_type -> memos.api.v1.UserWebhook + 30, // 84: memos.api.v1.UserService.UpdateUserWebhook:output_type -> memos.api.v1.UserWebhook + 51, // 85: memos.api.v1.UserService.DeleteUserWebhook:output_type -> google.protobuf.Empty + 38, // 86: memos.api.v1.UserService.ListUserNotifications:output_type -> memos.api.v1.ListUserNotificationsResponse + 36, // 87: memos.api.v1.UserService.UpdateUserNotification:output_type -> memos.api.v1.UserNotification + 51, // 88: memos.api.v1.UserService.DeleteUserNotification:output_type -> google.protobuf.Empty + 66, // [66:89] is the sub-list for method output_type + 43, // [43:66] is the sub-list for method input_type + 43, // [43:43] is the sub-list for extension type_name + 43, // [43:43] is the sub-list for extension extendee + 0, // [0:43] is the sub-list for field type_name } func init() { file_api_v1_user_service_proto_init() } @@ -2911,13 +3376,14 @@ func file_api_v1_user_service_proto_init() { (*UserSetting_AccessTokensSetting_)(nil), (*UserSetting_WebhooksSetting_)(nil), } + file_api_v1_user_service_proto_msgTypes[32].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_v1_user_service_proto_rawDesc), len(file_api_v1_user_service_proto_rawDesc)), - NumEnums: 2, - NumMessages: 39, + NumEnums: 4, + NumMessages: 44, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/gen/api/v1/user_service.pb.gw.go b/proto/gen/api/v1/user_service.pb.gw.go index 7e5c9dafe..bf4051c69 100644 --- a/proto/gen/api/v1/user_service.pb.gw.go +++ b/proto/gen/api/v1/user_service.pb.gw.go @@ -1003,6 +1003,179 @@ func local_request_UserService_DeleteUserWebhook_0(ctx context.Context, marshale return msg, metadata, err } +var filter_UserService_ListUserNotifications_0 = &utilities.DoubleArray{Encoding: map[string]int{"parent": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} + +func request_UserService_ListUserNotifications_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq ListUserNotificationsRequest + metadata runtime.ServerMetadata + err error + ) + if req.Body != nil { + _, _ = io.Copy(io.Discard, req.Body) + } + val, ok := pathParams["parent"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "parent") + } + protoReq.Parent, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err) + } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_UserService_ListUserNotifications_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.ListUserNotifications(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_UserService_ListUserNotifications_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq ListUserNotificationsRequest + metadata runtime.ServerMetadata + err error + ) + val, ok := pathParams["parent"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "parent") + } + protoReq.Parent, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err) + } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_UserService_ListUserNotifications_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.ListUserNotifications(ctx, &protoReq) + return msg, metadata, err +} + +var filter_UserService_UpdateUserNotification_0 = &utilities.DoubleArray{Encoding: map[string]int{"notification": 0, "name": 1}, Base: []int{1, 2, 1, 0, 0}, Check: []int{0, 1, 2, 3, 2}} + +func request_UserService_UpdateUserNotification_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq UpdateUserNotificationRequest + metadata runtime.ServerMetadata + err error + ) + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Notification); err != nil && !errors.Is(err, io.EOF) { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if req.Body != nil { + _, _ = io.Copy(io.Discard, req.Body) + } + if protoReq.UpdateMask == nil || len(protoReq.UpdateMask.GetPaths()) == 0 { + if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), protoReq.Notification); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } else { + protoReq.UpdateMask = fieldMask + } + } + val, ok := pathParams["notification.name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "notification.name") + } + err = runtime.PopulateFieldFromPath(&protoReq, "notification.name", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "notification.name", err) + } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_UserService_UpdateUserNotification_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.UpdateUserNotification(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_UserService_UpdateUserNotification_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq UpdateUserNotificationRequest + metadata runtime.ServerMetadata + err error + ) + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Notification); err != nil && !errors.Is(err, io.EOF) { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if protoReq.UpdateMask == nil || len(protoReq.UpdateMask.GetPaths()) == 0 { + if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), protoReq.Notification); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } else { + protoReq.UpdateMask = fieldMask + } + } + val, ok := pathParams["notification.name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "notification.name") + } + err = runtime.PopulateFieldFromPath(&protoReq, "notification.name", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "notification.name", err) + } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_UserService_UpdateUserNotification_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.UpdateUserNotification(ctx, &protoReq) + return msg, metadata, err +} + +func request_UserService_DeleteUserNotification_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq DeleteUserNotificationRequest + metadata runtime.ServerMetadata + err error + ) + if req.Body != nil { + _, _ = io.Copy(io.Discard, req.Body) + } + val, ok := pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + msg, err := client.DeleteUserNotification(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_UserService_DeleteUserNotification_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq DeleteUserNotificationRequest + metadata runtime.ServerMetadata + err error + ) + val, ok := pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + msg, err := server.DeleteUserNotification(ctx, &protoReq) + return msg, metadata, err +} + // RegisterUserServiceHandlerServer registers the http handlers for service UserService to "mux". // UnaryRPC :call UserServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -1409,6 +1582,66 @@ func RegisterUserServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux } forward_UserService_DeleteUserWebhook_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) + mux.Handle(http.MethodGet, pattern_UserService_ListUserNotifications_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/ListUserNotifications", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/notifications")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_UserService_ListUserNotifications_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_UserService_ListUserNotifications_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodPatch, pattern_UserService_UpdateUserNotification_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/UpdateUserNotification", runtime.WithHTTPPathPattern("/api/v1/{notification.name=users/*/notifications/*}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_UserService_UpdateUserNotification_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_UserService_UpdateUserNotification_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodDelete, pattern_UserService_DeleteUserNotification_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/DeleteUserNotification", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/notifications/*}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_UserService_DeleteUserNotification_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_UserService_DeleteUserNotification_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) return nil } @@ -1789,51 +2022,108 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux } forward_UserService_DeleteUserWebhook_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) + mux.Handle(http.MethodGet, pattern_UserService_ListUserNotifications_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/ListUserNotifications", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/notifications")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_UserService_ListUserNotifications_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_UserService_ListUserNotifications_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodPatch, pattern_UserService_UpdateUserNotification_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/UpdateUserNotification", runtime.WithHTTPPathPattern("/api/v1/{notification.name=users/*/notifications/*}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_UserService_UpdateUserNotification_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_UserService_UpdateUserNotification_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodDelete, pattern_UserService_DeleteUserNotification_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/DeleteUserNotification", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/notifications/*}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_UserService_DeleteUserNotification_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_UserService_DeleteUserNotification_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) return nil } var ( - pattern_UserService_ListUsers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "")) - pattern_UserService_GetUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, "")) - pattern_UserService_CreateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "")) - pattern_UserService_UpdateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "user.name"}, "")) - pattern_UserService_DeleteUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, "")) - pattern_UserService_GetUserAvatar_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "name", "avatar"}, "")) - pattern_UserService_ListAllUserStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "stats")) - pattern_UserService_GetUserStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, "getStats")) - pattern_UserService_GetUserSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "settings", "name"}, "")) - pattern_UserService_UpdateUserSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "settings", "setting.name"}, "")) - pattern_UserService_ListUserSettings_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "settings"}, "")) - pattern_UserService_ListUserAccessTokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "accessTokens"}, "")) - pattern_UserService_CreateUserAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "accessTokens"}, "")) - pattern_UserService_DeleteUserAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "accessTokens", "name"}, "")) - pattern_UserService_ListUserSessions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "sessions"}, "")) - pattern_UserService_RevokeUserSession_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "sessions", "name"}, "")) - pattern_UserService_ListUserWebhooks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "webhooks"}, "")) - pattern_UserService_CreateUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "webhooks"}, "")) - pattern_UserService_UpdateUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "webhook.name"}, "")) - pattern_UserService_DeleteUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "name"}, "")) + pattern_UserService_ListUsers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "")) + pattern_UserService_GetUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, "")) + pattern_UserService_CreateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "")) + pattern_UserService_UpdateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "user.name"}, "")) + pattern_UserService_DeleteUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, "")) + pattern_UserService_GetUserAvatar_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "name", "avatar"}, "")) + pattern_UserService_ListAllUserStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "stats")) + pattern_UserService_GetUserStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, "getStats")) + pattern_UserService_GetUserSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "settings", "name"}, "")) + pattern_UserService_UpdateUserSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "settings", "setting.name"}, "")) + pattern_UserService_ListUserSettings_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "settings"}, "")) + pattern_UserService_ListUserAccessTokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "accessTokens"}, "")) + pattern_UserService_CreateUserAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "accessTokens"}, "")) + pattern_UserService_DeleteUserAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "accessTokens", "name"}, "")) + pattern_UserService_ListUserSessions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "sessions"}, "")) + pattern_UserService_RevokeUserSession_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "sessions", "name"}, "")) + pattern_UserService_ListUserWebhooks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "webhooks"}, "")) + pattern_UserService_CreateUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "webhooks"}, "")) + pattern_UserService_UpdateUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "webhook.name"}, "")) + pattern_UserService_DeleteUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "name"}, "")) + pattern_UserService_ListUserNotifications_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "notifications"}, "")) + pattern_UserService_UpdateUserNotification_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "notifications", "notification.name"}, "")) + pattern_UserService_DeleteUserNotification_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "notifications", "name"}, "")) ) var ( - forward_UserService_ListUsers_0 = runtime.ForwardResponseMessage - forward_UserService_GetUser_0 = runtime.ForwardResponseMessage - forward_UserService_CreateUser_0 = runtime.ForwardResponseMessage - forward_UserService_UpdateUser_0 = runtime.ForwardResponseMessage - forward_UserService_DeleteUser_0 = runtime.ForwardResponseMessage - forward_UserService_GetUserAvatar_0 = runtime.ForwardResponseMessage - forward_UserService_ListAllUserStats_0 = runtime.ForwardResponseMessage - forward_UserService_GetUserStats_0 = runtime.ForwardResponseMessage - forward_UserService_GetUserSetting_0 = runtime.ForwardResponseMessage - forward_UserService_UpdateUserSetting_0 = runtime.ForwardResponseMessage - forward_UserService_ListUserSettings_0 = runtime.ForwardResponseMessage - forward_UserService_ListUserAccessTokens_0 = runtime.ForwardResponseMessage - forward_UserService_CreateUserAccessToken_0 = runtime.ForwardResponseMessage - forward_UserService_DeleteUserAccessToken_0 = runtime.ForwardResponseMessage - forward_UserService_ListUserSessions_0 = runtime.ForwardResponseMessage - forward_UserService_RevokeUserSession_0 = runtime.ForwardResponseMessage - forward_UserService_ListUserWebhooks_0 = runtime.ForwardResponseMessage - forward_UserService_CreateUserWebhook_0 = runtime.ForwardResponseMessage - forward_UserService_UpdateUserWebhook_0 = runtime.ForwardResponseMessage - forward_UserService_DeleteUserWebhook_0 = runtime.ForwardResponseMessage + forward_UserService_ListUsers_0 = runtime.ForwardResponseMessage + forward_UserService_GetUser_0 = runtime.ForwardResponseMessage + forward_UserService_CreateUser_0 = runtime.ForwardResponseMessage + forward_UserService_UpdateUser_0 = runtime.ForwardResponseMessage + forward_UserService_DeleteUser_0 = runtime.ForwardResponseMessage + forward_UserService_GetUserAvatar_0 = runtime.ForwardResponseMessage + forward_UserService_ListAllUserStats_0 = runtime.ForwardResponseMessage + forward_UserService_GetUserStats_0 = runtime.ForwardResponseMessage + forward_UserService_GetUserSetting_0 = runtime.ForwardResponseMessage + forward_UserService_UpdateUserSetting_0 = runtime.ForwardResponseMessage + forward_UserService_ListUserSettings_0 = runtime.ForwardResponseMessage + forward_UserService_ListUserAccessTokens_0 = runtime.ForwardResponseMessage + forward_UserService_CreateUserAccessToken_0 = runtime.ForwardResponseMessage + forward_UserService_DeleteUserAccessToken_0 = runtime.ForwardResponseMessage + forward_UserService_ListUserSessions_0 = runtime.ForwardResponseMessage + forward_UserService_RevokeUserSession_0 = runtime.ForwardResponseMessage + forward_UserService_ListUserWebhooks_0 = runtime.ForwardResponseMessage + forward_UserService_CreateUserWebhook_0 = runtime.ForwardResponseMessage + forward_UserService_UpdateUserWebhook_0 = runtime.ForwardResponseMessage + forward_UserService_DeleteUserWebhook_0 = runtime.ForwardResponseMessage + forward_UserService_ListUserNotifications_0 = runtime.ForwardResponseMessage + forward_UserService_UpdateUserNotification_0 = runtime.ForwardResponseMessage + forward_UserService_DeleteUserNotification_0 = runtime.ForwardResponseMessage ) diff --git a/proto/gen/api/v1/user_service_grpc.pb.go b/proto/gen/api/v1/user_service_grpc.pb.go index 575241ead..1d6527406 100644 --- a/proto/gen/api/v1/user_service_grpc.pb.go +++ b/proto/gen/api/v1/user_service_grpc.pb.go @@ -21,26 +21,29 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - UserService_ListUsers_FullMethodName = "/memos.api.v1.UserService/ListUsers" - UserService_GetUser_FullMethodName = "/memos.api.v1.UserService/GetUser" - UserService_CreateUser_FullMethodName = "/memos.api.v1.UserService/CreateUser" - UserService_UpdateUser_FullMethodName = "/memos.api.v1.UserService/UpdateUser" - UserService_DeleteUser_FullMethodName = "/memos.api.v1.UserService/DeleteUser" - UserService_GetUserAvatar_FullMethodName = "/memos.api.v1.UserService/GetUserAvatar" - UserService_ListAllUserStats_FullMethodName = "/memos.api.v1.UserService/ListAllUserStats" - UserService_GetUserStats_FullMethodName = "/memos.api.v1.UserService/GetUserStats" - UserService_GetUserSetting_FullMethodName = "/memos.api.v1.UserService/GetUserSetting" - UserService_UpdateUserSetting_FullMethodName = "/memos.api.v1.UserService/UpdateUserSetting" - UserService_ListUserSettings_FullMethodName = "/memos.api.v1.UserService/ListUserSettings" - UserService_ListUserAccessTokens_FullMethodName = "/memos.api.v1.UserService/ListUserAccessTokens" - UserService_CreateUserAccessToken_FullMethodName = "/memos.api.v1.UserService/CreateUserAccessToken" - UserService_DeleteUserAccessToken_FullMethodName = "/memos.api.v1.UserService/DeleteUserAccessToken" - UserService_ListUserSessions_FullMethodName = "/memos.api.v1.UserService/ListUserSessions" - UserService_RevokeUserSession_FullMethodName = "/memos.api.v1.UserService/RevokeUserSession" - UserService_ListUserWebhooks_FullMethodName = "/memos.api.v1.UserService/ListUserWebhooks" - UserService_CreateUserWebhook_FullMethodName = "/memos.api.v1.UserService/CreateUserWebhook" - UserService_UpdateUserWebhook_FullMethodName = "/memos.api.v1.UserService/UpdateUserWebhook" - UserService_DeleteUserWebhook_FullMethodName = "/memos.api.v1.UserService/DeleteUserWebhook" + UserService_ListUsers_FullMethodName = "/memos.api.v1.UserService/ListUsers" + UserService_GetUser_FullMethodName = "/memos.api.v1.UserService/GetUser" + UserService_CreateUser_FullMethodName = "/memos.api.v1.UserService/CreateUser" + UserService_UpdateUser_FullMethodName = "/memos.api.v1.UserService/UpdateUser" + UserService_DeleteUser_FullMethodName = "/memos.api.v1.UserService/DeleteUser" + UserService_GetUserAvatar_FullMethodName = "/memos.api.v1.UserService/GetUserAvatar" + UserService_ListAllUserStats_FullMethodName = "/memos.api.v1.UserService/ListAllUserStats" + UserService_GetUserStats_FullMethodName = "/memos.api.v1.UserService/GetUserStats" + UserService_GetUserSetting_FullMethodName = "/memos.api.v1.UserService/GetUserSetting" + UserService_UpdateUserSetting_FullMethodName = "/memos.api.v1.UserService/UpdateUserSetting" + UserService_ListUserSettings_FullMethodName = "/memos.api.v1.UserService/ListUserSettings" + UserService_ListUserAccessTokens_FullMethodName = "/memos.api.v1.UserService/ListUserAccessTokens" + UserService_CreateUserAccessToken_FullMethodName = "/memos.api.v1.UserService/CreateUserAccessToken" + UserService_DeleteUserAccessToken_FullMethodName = "/memos.api.v1.UserService/DeleteUserAccessToken" + UserService_ListUserSessions_FullMethodName = "/memos.api.v1.UserService/ListUserSessions" + UserService_RevokeUserSession_FullMethodName = "/memos.api.v1.UserService/RevokeUserSession" + UserService_ListUserWebhooks_FullMethodName = "/memos.api.v1.UserService/ListUserWebhooks" + UserService_CreateUserWebhook_FullMethodName = "/memos.api.v1.UserService/CreateUserWebhook" + UserService_UpdateUserWebhook_FullMethodName = "/memos.api.v1.UserService/UpdateUserWebhook" + UserService_DeleteUserWebhook_FullMethodName = "/memos.api.v1.UserService/DeleteUserWebhook" + UserService_ListUserNotifications_FullMethodName = "/memos.api.v1.UserService/ListUserNotifications" + UserService_UpdateUserNotification_FullMethodName = "/memos.api.v1.UserService/UpdateUserNotification" + UserService_DeleteUserNotification_FullMethodName = "/memos.api.v1.UserService/DeleteUserNotification" ) // UserServiceClient is the client API for UserService service. @@ -90,6 +93,12 @@ type UserServiceClient interface { UpdateUserWebhook(ctx context.Context, in *UpdateUserWebhookRequest, opts ...grpc.CallOption) (*UserWebhook, error) // DeleteUserWebhook deletes a webhook for a user. DeleteUserWebhook(ctx context.Context, in *DeleteUserWebhookRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + // ListUserNotifications lists notifications for a user. + ListUserNotifications(ctx context.Context, in *ListUserNotificationsRequest, opts ...grpc.CallOption) (*ListUserNotificationsResponse, error) + // UpdateUserNotification updates a notification. + UpdateUserNotification(ctx context.Context, in *UpdateUserNotificationRequest, opts ...grpc.CallOption) (*UserNotification, error) + // DeleteUserNotification deletes a notification. + DeleteUserNotification(ctx context.Context, in *DeleteUserNotificationRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) } type userServiceClient struct { @@ -300,6 +309,36 @@ func (c *userServiceClient) DeleteUserWebhook(ctx context.Context, in *DeleteUse return out, nil } +func (c *userServiceClient) ListUserNotifications(ctx context.Context, in *ListUserNotificationsRequest, opts ...grpc.CallOption) (*ListUserNotificationsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListUserNotificationsResponse) + err := c.cc.Invoke(ctx, UserService_ListUserNotifications_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *userServiceClient) UpdateUserNotification(ctx context.Context, in *UpdateUserNotificationRequest, opts ...grpc.CallOption) (*UserNotification, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UserNotification) + err := c.cc.Invoke(ctx, UserService_UpdateUserNotification_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *userServiceClient) DeleteUserNotification(ctx context.Context, in *DeleteUserNotificationRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, UserService_DeleteUserNotification_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // UserServiceServer is the server API for UserService service. // All implementations must embed UnimplementedUserServiceServer // for forward compatibility. @@ -347,6 +386,12 @@ type UserServiceServer interface { UpdateUserWebhook(context.Context, *UpdateUserWebhookRequest) (*UserWebhook, error) // DeleteUserWebhook deletes a webhook for a user. DeleteUserWebhook(context.Context, *DeleteUserWebhookRequest) (*emptypb.Empty, error) + // ListUserNotifications lists notifications for a user. + ListUserNotifications(context.Context, *ListUserNotificationsRequest) (*ListUserNotificationsResponse, error) + // UpdateUserNotification updates a notification. + UpdateUserNotification(context.Context, *UpdateUserNotificationRequest) (*UserNotification, error) + // DeleteUserNotification deletes a notification. + DeleteUserNotification(context.Context, *DeleteUserNotificationRequest) (*emptypb.Empty, error) mustEmbedUnimplementedUserServiceServer() } @@ -417,6 +462,15 @@ func (UnimplementedUserServiceServer) UpdateUserWebhook(context.Context, *Update func (UnimplementedUserServiceServer) DeleteUserWebhook(context.Context, *DeleteUserWebhookRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteUserWebhook not implemented") } +func (UnimplementedUserServiceServer) ListUserNotifications(context.Context, *ListUserNotificationsRequest) (*ListUserNotificationsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListUserNotifications not implemented") +} +func (UnimplementedUserServiceServer) UpdateUserNotification(context.Context, *UpdateUserNotificationRequest) (*UserNotification, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateUserNotification not implemented") +} +func (UnimplementedUserServiceServer) DeleteUserNotification(context.Context, *DeleteUserNotificationRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteUserNotification not implemented") +} func (UnimplementedUserServiceServer) mustEmbedUnimplementedUserServiceServer() {} func (UnimplementedUserServiceServer) testEmbeddedByValue() {} @@ -798,6 +852,60 @@ func _UserService_DeleteUserWebhook_Handler(srv interface{}, ctx context.Context return interceptor(ctx, in, info, handler) } +func _UserService_ListUserNotifications_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListUserNotificationsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServiceServer).ListUserNotifications(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: UserService_ListUserNotifications_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServiceServer).ListUserNotifications(ctx, req.(*ListUserNotificationsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _UserService_UpdateUserNotification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateUserNotificationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServiceServer).UpdateUserNotification(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: UserService_UpdateUserNotification_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServiceServer).UpdateUserNotification(ctx, req.(*UpdateUserNotificationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _UserService_DeleteUserNotification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteUserNotificationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServiceServer).DeleteUserNotification(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: UserService_DeleteUserNotification_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServiceServer).DeleteUserNotification(ctx, req.(*DeleteUserNotificationRequest)) + } + return interceptor(ctx, in, info, handler) +} + // UserService_ServiceDesc is the grpc.ServiceDesc for UserService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -885,6 +993,18 @@ var UserService_ServiceDesc = grpc.ServiceDesc{ MethodName: "DeleteUserWebhook", Handler: _UserService_DeleteUserWebhook_Handler, }, + { + MethodName: "ListUserNotifications", + Handler: _UserService_ListUserNotifications_Handler, + }, + { + MethodName: "UpdateUserNotification", + Handler: _UserService_UpdateUserNotification_Handler, + }, + { + MethodName: "DeleteUserNotification", + Handler: _UserService_DeleteUserNotification_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "api/v1/user_service.proto", diff --git a/proto/gen/openapi.yaml b/proto/gen/openapi.yaml index 99960bfb7..c9dfc653c 100644 --- a/proto/gen/openapi.yaml +++ b/proto/gen/openapi.yaml @@ -442,71 +442,6 @@ paths: application/json: schema: $ref: '#/components/schemas/Status' - /api/v1/inboxes/{inboxe}: - delete: - tags: - - InboxService - description: DeleteInbox deletes an inbox. - operationId: InboxService_DeleteInbox - parameters: - - name: inboxe - in: path - description: The inboxe id. - required: true - schema: - type: string - responses: - "200": - description: OK - content: {} - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - patch: - tags: - - InboxService - description: UpdateInbox updates an inbox. - operationId: InboxService_UpdateInbox - parameters: - - name: inboxe - in: path - description: The inboxe id. - required: true - schema: - type: string - - name: updateMask - in: query - description: Required. The list of fields to update. - schema: - type: string - format: field-mask - - name: allowMissing - in: query - description: Optional. If set to true, allows updating missing fields. - schema: - type: boolean - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/Inbox' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/Inbox' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' /api/v1/memos: get: tags: @@ -1329,12 +1264,12 @@ paths: application/json: schema: $ref: '#/components/schemas/Status' - /api/v1/users/{user}/inboxes: + /api/v1/users/{user}/notifications: get: tags: - - InboxService - description: ListInboxes lists inboxes for a user. - operationId: InboxService_ListInboxes + - UserService + description: ListUserNotifications lists notifications for a user. + operationId: UserService_ListUserNotifications parameters: - name: user in: path @@ -1344,44 +1279,95 @@ paths: type: string - name: pageSize in: query - description: |- - Optional. The maximum number of inboxes to return. - The service may return fewer than this value. - If unspecified, at most 50 inboxes will be returned. - The maximum value is 1000; values above 1000 will be coerced to 1000. schema: type: integer format: int32 - name: pageToken in: query - description: |- - Optional. A page token, received from a previous `ListInboxes` call. - Provide this to retrieve the subsequent page. schema: type: string - name: filter in: query - description: |- - Optional. Filter to apply to the list results. - Example: "status=UNREAD" or "type=MEMO_COMMENT" - Supported operators: =, != - Supported fields: status, type, sender, create_time schema: type: string - - name: orderBy + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListUserNotificationsResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/users/{user}/notifications/{notification}: + delete: + tags: + - UserService + description: DeleteUserNotification deletes a notification. + operationId: UserService_DeleteUserNotification + parameters: + - name: user + in: path + description: The user id. + required: true + schema: + type: string + - name: notification + in: path + description: The notification id. + required: true + schema: + type: string + responses: + "200": + description: OK + content: {} + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + patch: + tags: + - UserService + description: UpdateUserNotification updates a notification. + operationId: UserService_UpdateUserNotification + parameters: + - name: user + in: path + description: The user id. + required: true + schema: + type: string + - name: notification + in: path + description: The notification id. + required: true + schema: + type: string + - name: updateMask in: query - description: |- - Optional. The order to sort results by. - Example: "create_time desc" or "status asc" schema: type: string + format: field-mask + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UserNotification' + required: true responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/ListInboxesResponse' + $ref: '#/components/schemas/UserNotification' default: description: Default error response content: @@ -2039,7 +2025,6 @@ components: enum: - TYPE_UNSPECIFIED - MEMO_COMMENT - - VERSION_UPDATE type: string description: The type of the activity. format: enum @@ -2264,52 +2249,6 @@ components: properties: oauth2Config: $ref: '#/components/schemas/OAuth2Config' - Inbox: - type: object - properties: - name: - type: string - description: |- - The resource name of the inbox. - Format: inboxes/{inbox} - sender: - readOnly: true - type: string - description: |- - The sender of the inbox notification. - Format: users/{user} - receiver: - readOnly: true - type: string - description: |- - The receiver of the inbox notification. - Format: users/{user} - status: - enum: - - STATUS_UNSPECIFIED - - UNREAD - - ARCHIVED - type: string - description: The status of the inbox notification. - format: enum - createTime: - readOnly: true - type: string - description: Output only. The creation timestamp. - format: date-time - type: - readOnly: true - enum: - - TYPE_UNSPECIFIED - - MEMO_COMMENT - - VERSION_UPDATE - type: string - description: The type of the inbox notification. - format: enum - activityId: - type: integer - description: Optional. The activity ID associated with this inbox notification. - format: int32 ListActivitiesResponse: type: object properties: @@ -2357,23 +2296,6 @@ components: items: $ref: '#/components/schemas/IdentityProvider' description: The list of identity providers. - ListInboxesResponse: - type: object - properties: - inboxes: - type: array - items: - $ref: '#/components/schemas/Inbox' - description: The list of inboxes. - nextPageToken: - type: string - description: |- - A token that can be sent as `page_token` to retrieve the next page. - If this field is omitted, there are no subsequent pages. - totalSize: - type: integer - description: The total count of inboxes (may be approximate). - format: int32 ListMemoAttachmentsResponse: type: object properties: @@ -2462,6 +2384,15 @@ components: type: integer description: The total count of access tokens. format: int32 + ListUserNotificationsResponse: + type: object + properties: + notifications: + type: array + items: + $ref: '#/components/schemas/UserNotification' + nextPageToken: + type: string ListUserSessionsResponse: type: object properties: @@ -2903,6 +2834,46 @@ components: description: Optional. The expiration timestamp. format: date-time description: User access token message + UserNotification: + type: object + properties: + name: + readOnly: true + type: string + description: |- + The resource name of the notification. + Format: users/{user}/notifications/{notification} + sender: + readOnly: true + type: string + description: |- + The sender of the notification. + Format: users/{user} + status: + enum: + - STATUS_UNSPECIFIED + - UNREAD + - ARCHIVED + type: string + description: The status of the notification. + format: enum + createTime: + readOnly: true + type: string + description: The creation timestamp. + format: date-time + type: + readOnly: true + enum: + - TYPE_UNSPECIFIED + - MEMO_COMMENT + type: string + description: The type of the notification. + format: enum + activityId: + type: integer + description: The activity ID associated with this notification. + format: int32 UserSession: type: object properties: @@ -3223,7 +3194,6 @@ tags: - name: AttachmentService - name: AuthService - name: IdentityProviderService - - name: InboxService - name: MemoService - name: ShortcutService - name: UserService diff --git a/proto/gen/store/inbox.pb.go b/proto/gen/store/inbox.pb.go index 9e2f848c9..f9b5c65ff 100644 --- a/proto/gen/store/inbox.pb.go +++ b/proto/gen/store/inbox.pb.go @@ -25,8 +25,8 @@ type InboxMessage_Type int32 const ( InboxMessage_TYPE_UNSPECIFIED InboxMessage_Type = 0 - InboxMessage_MEMO_COMMENT InboxMessage_Type = 1 - InboxMessage_VERSION_UPDATE InboxMessage_Type = 2 + // Memo comment notification. + InboxMessage_MEMO_COMMENT InboxMessage_Type = 1 ) // Enum value maps for InboxMessage_Type. @@ -34,12 +34,10 @@ var ( InboxMessage_Type_name = map[int32]string{ 0: "TYPE_UNSPECIFIED", 1: "MEMO_COMMENT", - 2: "VERSION_UPDATE", } InboxMessage_Type_value = map[string]int32{ "TYPE_UNSPECIFIED": 0, "MEMO_COMMENT": 1, - "VERSION_UPDATE": 2, } ) @@ -71,9 +69,11 @@ func (InboxMessage_Type) EnumDescriptor() ([]byte, []int) { } type InboxMessage struct { - state protoimpl.MessageState `protogen:"open.v1"` - Type InboxMessage_Type `protobuf:"varint,1,opt,name=type,proto3,enum=memos.store.InboxMessage_Type" json:"type,omitempty"` - ActivityId *int32 `protobuf:"varint,2,opt,name=activity_id,json=activityId,proto3,oneof" json:"activity_id,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + // The type of the inbox message. + Type InboxMessage_Type `protobuf:"varint,1,opt,name=type,proto3,enum=memos.store.InboxMessage_Type" json:"type,omitempty"` + // The system-generated unique ID of related activity. + ActivityId *int32 `protobuf:"varint,2,opt,name=activity_id,json=activityId,proto3,oneof" json:"activity_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -126,15 +126,14 @@ var File_store_inbox_proto protoreflect.FileDescriptor const file_store_inbox_proto_rawDesc = "" + "\n" + - "\x11store/inbox.proto\x12\vmemos.store\"\xbc\x01\n" + + "\x11store/inbox.proto\x12\vmemos.store\"\xa8\x01\n" + "\fInboxMessage\x122\n" + "\x04type\x18\x01 \x01(\x0e2\x1e.memos.store.InboxMessage.TypeR\x04type\x12$\n" + "\vactivity_id\x18\x02 \x01(\x05H\x00R\n" + - "activityId\x88\x01\x01\"B\n" + + "activityId\x88\x01\x01\".\n" + "\x04Type\x12\x14\n" + "\x10TYPE_UNSPECIFIED\x10\x00\x12\x10\n" + - "\fMEMO_COMMENT\x10\x01\x12\x12\n" + - "\x0eVERSION_UPDATE\x10\x02B\x0e\n" + + "\fMEMO_COMMENT\x10\x01B\x0e\n" + "\f_activity_idB\x95\x01\n" + "\x0fcom.memos.storeB\n" + "InboxProtoP\x01Z)github.com/usememos/memos/proto/gen/store\xa2\x02\x03MSX\xaa\x02\vMemos.Store\xca\x02\vMemos\\Store\xe2\x02\x17Memos\\Store\\GPBMetadata\xea\x02\fMemos::Storeb\x06proto3" diff --git a/proto/store/inbox.proto b/proto/store/inbox.proto index f7f708249..002c6697d 100644 --- a/proto/store/inbox.proto +++ b/proto/store/inbox.proto @@ -5,11 +5,14 @@ package memos.store; option go_package = "gen/store"; message InboxMessage { + // The type of the inbox message. + Type type = 1; + // The system-generated unique ID of related activity. + optional int32 activity_id = 2; + enum Type { TYPE_UNSPECIFIED = 0; + // Memo comment notification. MEMO_COMMENT = 1; - VERSION_UPDATE = 2; } - Type type = 1; - optional int32 activity_id = 2; } diff --git a/server/router/api/v1/activity_service.go b/server/router/api/v1/activity_service.go index ae016905f..49cd1c6ed 100644 --- a/server/router/api/v1/activity_service.go +++ b/server/router/api/v1/activity_service.go @@ -64,6 +64,9 @@ func (s *APIV1Service) GetActivity(ctx context.Context, request *v1pb.GetActivit return activityMessage, nil } +// convertActivityFromStore converts a storage-layer activity to an API activity. +// This handles the mapping between internal activity representation and the public API, +// including proper type and level conversions. func (s *APIV1Service) convertActivityFromStore(ctx context.Context, activity *store.Activity) (*v1pb.Activity, error) { payload, err := s.convertActivityPayloadFromStore(ctx, activity.Payload) if err != nil { @@ -98,9 +101,12 @@ func (s *APIV1Service) convertActivityFromStore(ctx context.Context, activity *s }, nil } +// convertActivityPayloadFromStore converts a storage-layer activity payload to an API payload. +// This resolves references (e.g., memo IDs) to resource names for the API. func (s *APIV1Service) convertActivityPayloadFromStore(ctx context.Context, payload *storepb.ActivityPayload) (*v1pb.ActivityPayload, error) { v2Payload := &v1pb.ActivityPayload{} if payload.MemoComment != nil { + // Fetch the comment memo memo, err := s.Store.GetMemo(ctx, &store.FindMemo{ ID: &payload.MemoComment.MemoId, ExcludeContent: true, @@ -111,6 +117,8 @@ func (s *APIV1Service) convertActivityPayloadFromStore(ctx context.Context, payl if memo == nil { return nil, status.Errorf(codes.NotFound, "memo does not exist") } + + // Fetch the related memo (the one being commented on) relatedMemo, err := s.Store.GetMemo(ctx, &store.FindMemo{ ID: &payload.MemoComment.RelatedMemoId, ExcludeContent: true, @@ -118,6 +126,7 @@ func (s *APIV1Service) convertActivityPayloadFromStore(ctx context.Context, payl if err != nil { return nil, status.Errorf(codes.Internal, "failed to get related memo: %v", err) } + v2Payload.Payload = &v1pb.ActivityPayload_MemoComment{ MemoComment: &v1pb.ActivityMemoCommentPayload{ Memo: fmt.Sprintf("%s%s", MemoNamePrefix, memo.UID), diff --git a/server/router/api/v1/inbox_service.go b/server/router/api/v1/inbox_service.go deleted file mode 100644 index 223be9d04..000000000 --- a/server/router/api/v1/inbox_service.go +++ /dev/null @@ -1,224 +0,0 @@ -package v1 - -import ( - "context" - "fmt" - "time" - - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/types/known/emptypb" - "google.golang.org/protobuf/types/known/timestamppb" - - v1pb "github.com/usememos/memos/proto/gen/api/v1" - "github.com/usememos/memos/store" -) - -func (s *APIV1Service) ListInboxes(ctx context.Context, request *v1pb.ListInboxesRequest) (*v1pb.ListInboxesResponse, error) { - // Extract user ID from parent resource name - userID, err := ExtractUserIDFromName(request.Parent) - if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "invalid parent name %q: %v", request.Parent, err) - } - - // Get current user for authorization - currentUser, err := s.GetCurrentUser(ctx) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to get current user") - } - if currentUser == nil { - return nil, status.Errorf(codes.Unauthenticated, "user not authenticated") - } - - // Check if current user can access the requested user's inboxes - if currentUser.ID != userID { - // Only allow hosts and admins to access other users' inboxes - if currentUser.Role != store.RoleHost && currentUser.Role != store.RoleAdmin { - return nil, status.Errorf(codes.PermissionDenied, "cannot access inboxes for user %q", request.Parent) - } - } - - var limit, offset int - if request.PageToken != "" { - var pageToken v1pb.PageToken - if err := unmarshalPageToken(request.PageToken, &pageToken); err != nil { - return nil, status.Errorf(codes.InvalidArgument, "invalid page token: %v", err) - } - limit = int(pageToken.Limit) - offset = int(pageToken.Offset) - } else { - limit = int(request.PageSize) - } - if limit <= 0 { - limit = DefaultPageSize - } - if limit > MaxPageSize { - limit = MaxPageSize - } - limitPlusOne := limit + 1 - - findInbox := &store.FindInbox{ - ReceiverID: &userID, - Limit: &limitPlusOne, - Offset: &offset, - } - - inboxes, err := s.Store.ListInboxes(ctx, findInbox) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to list inboxes: %v", err) - } - - inboxMessages := []*v1pb.Inbox{} - nextPageToken := "" - if len(inboxes) == limitPlusOne { - inboxes = inboxes[:limit] - nextPageToken, err = getPageToken(limit, offset+limit) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to get next page token: %v", err) - } - } - for _, inbox := range inboxes { - inboxMessage := convertInboxFromStore(inbox) - if inboxMessage.Type == v1pb.Inbox_TYPE_UNSPECIFIED { - continue - } - inboxMessages = append(inboxMessages, inboxMessage) - } - - response := &v1pb.ListInboxesResponse{ - Inboxes: inboxMessages, - NextPageToken: nextPageToken, - TotalSize: int32(len(inboxMessages)), // For now, use actual returned count - } - return response, nil -} - -func (s *APIV1Service) UpdateInbox(ctx context.Context, request *v1pb.UpdateInboxRequest) (*v1pb.Inbox, error) { - if request.UpdateMask == nil || len(request.UpdateMask.Paths) == 0 { - return nil, status.Errorf(codes.InvalidArgument, "update mask is required") - } - - inboxID, err := ExtractInboxIDFromName(request.Inbox.Name) - if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "invalid inbox name %q: %v", request.Inbox.Name, err) - } - - // Get current user for authorization - currentUser, err := s.GetCurrentUser(ctx) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to get current user") - } - if currentUser == nil { - return nil, status.Errorf(codes.Unauthenticated, "user not authenticated") - } - - // Get the existing inbox to verify ownership - inboxes, err := s.Store.ListInboxes(ctx, &store.FindInbox{ - ID: &inboxID, - }) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to get inbox: %v", err) - } - if len(inboxes) == 0 { - return nil, status.Errorf(codes.NotFound, "inbox %q not found", request.Inbox.Name) - } - existingInbox := inboxes[0] - - // Check if current user can update this inbox (must be the receiver) - if currentUser.ID != existingInbox.ReceiverID { - return nil, status.Errorf(codes.PermissionDenied, "cannot update inbox for another user") - } - - update := &store.UpdateInbox{ - ID: inboxID, - } - for _, field := range request.UpdateMask.Paths { - if field == "status" { - if request.Inbox.Status == v1pb.Inbox_STATUS_UNSPECIFIED { - return nil, status.Errorf(codes.InvalidArgument, "status cannot be unspecified") - } - update.Status = convertInboxStatusToStore(request.Inbox.Status) - } else { - return nil, status.Errorf(codes.InvalidArgument, "unsupported field in update mask: %q", field) - } - } - - inbox, err := s.Store.UpdateInbox(ctx, update) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to update inbox: %v", err) - } - - return convertInboxFromStore(inbox), nil -} - -func (s *APIV1Service) DeleteInbox(ctx context.Context, request *v1pb.DeleteInboxRequest) (*emptypb.Empty, error) { - inboxID, err := ExtractInboxIDFromName(request.Name) - if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "invalid inbox name %q: %v", request.Name, err) - } - - // Get current user for authorization - currentUser, err := s.GetCurrentUser(ctx) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to get current user") - } - if currentUser == nil { - return nil, status.Errorf(codes.Unauthenticated, "user not authenticated") - } - - // Get the existing inbox to verify ownership - inboxes, err := s.Store.ListInboxes(ctx, &store.FindInbox{ - ID: &inboxID, - }) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to get inbox: %v", err) - } - if len(inboxes) == 0 { - return nil, status.Errorf(codes.NotFound, "inbox %q not found", request.Name) - } - existingInbox := inboxes[0] - - // Check if current user can delete this inbox (must be the receiver) - if currentUser.ID != existingInbox.ReceiverID { - return nil, status.Errorf(codes.PermissionDenied, "cannot delete inbox for another user") - } - - if err := s.Store.DeleteInbox(ctx, &store.DeleteInbox{ - ID: inboxID, - }); err != nil { - return nil, status.Errorf(codes.Internal, "failed to delete inbox: %v", err) - } - return &emptypb.Empty{}, nil -} - -func convertInboxFromStore(inbox *store.Inbox) *v1pb.Inbox { - return &v1pb.Inbox{ - Name: fmt.Sprintf("%s%d", InboxNamePrefix, inbox.ID), - Sender: fmt.Sprintf("%s%d", UserNamePrefix, inbox.SenderID), - Receiver: fmt.Sprintf("%s%d", UserNamePrefix, inbox.ReceiverID), - Status: convertInboxStatusFromStore(inbox.Status), - CreateTime: timestamppb.New(time.Unix(inbox.CreatedTs, 0)), - Type: v1pb.Inbox_Type(inbox.Message.Type), - ActivityId: inbox.Message.ActivityId, - } -} - -func convertInboxStatusFromStore(status store.InboxStatus) v1pb.Inbox_Status { - switch status { - case store.UNREAD: - return v1pb.Inbox_UNREAD - case store.ARCHIVED: - return v1pb.Inbox_ARCHIVED - default: - return v1pb.Inbox_STATUS_UNSPECIFIED - } -} - -func convertInboxStatusToStore(status v1pb.Inbox_Status) store.InboxStatus { - switch status { - case v1pb.Inbox_ARCHIVED: - return store.ARCHIVED - default: - return store.UNREAD - } -} diff --git a/server/router/api/v1/test/inbox_service_test.go b/server/router/api/v1/test/inbox_service_test.go deleted file mode 100644 index fdef31a5e..000000000 --- a/server/router/api/v1/test/inbox_service_test.go +++ /dev/null @@ -1,559 +0,0 @@ -package test - -import ( - "context" - "fmt" - "testing" - - "github.com/stretchr/testify/require" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/types/known/fieldmaskpb" - - v1pb "github.com/usememos/memos/proto/gen/api/v1" - storepb "github.com/usememos/memos/proto/gen/store" - "github.com/usememos/memos/store" -) - -func TestListInboxes(t *testing.T) { - ctx := context.Background() - - t.Run("ListInboxes success", func(t *testing.T) { - ts := NewTestService(t) - defer ts.Cleanup() - - // Create a user - user, err := ts.CreateRegularUser(ctx, "testuser") - require.NoError(t, err) - - // Set user context - userCtx := ts.CreateUserContext(ctx, user.ID) - - // List inboxes (should be empty initially) - req := &v1pb.ListInboxesRequest{ - Parent: fmt.Sprintf("users/%d", user.ID), - } - - resp, err := ts.Service.ListInboxes(userCtx, req) - require.NoError(t, err) - require.NotNil(t, resp) - require.Empty(t, resp.Inboxes) - require.Equal(t, int32(0), resp.TotalSize) - }) - - t.Run("ListInboxes with pagination", func(t *testing.T) { - ts := NewTestService(t) - defer ts.Cleanup() - - // Create a user - user, err := ts.CreateRegularUser(ctx, "testuser") - require.NoError(t, err) - - // Create some inbox entries - const systemBotID int32 = 0 - for i := 0; i < 3; i++ { - _, err := ts.Store.CreateInbox(ctx, &store.Inbox{ - SenderID: systemBotID, - ReceiverID: user.ID, - Status: store.UNREAD, - Message: &storepb.InboxMessage{ - Type: storepb.InboxMessage_MEMO_COMMENT, - }, - }) - require.NoError(t, err) - } - - // Set user context - userCtx := ts.CreateUserContext(ctx, user.ID) - - // List inboxes with page size limit - req := &v1pb.ListInboxesRequest{ - Parent: fmt.Sprintf("users/%d", user.ID), - PageSize: 2, - } - - resp, err := ts.Service.ListInboxes(userCtx, req) - require.NoError(t, err) - require.NotNil(t, resp) - require.Equal(t, 2, len(resp.Inboxes)) - require.NotEmpty(t, resp.NextPageToken) - }) - - t.Run("ListInboxes permission denied for different user", func(t *testing.T) { - ts := NewTestService(t) - defer ts.Cleanup() - - // Create two users - user1, err := ts.CreateRegularUser(ctx, "user1") - require.NoError(t, err) - user2, err := ts.CreateRegularUser(ctx, "user2") - require.NoError(t, err) - - // Set user1 context but try to list user2's inboxes - userCtx := ts.CreateUserContext(ctx, user1.ID) - - req := &v1pb.ListInboxesRequest{ - Parent: fmt.Sprintf("users/%d", user2.ID), - } - - _, err = ts.Service.ListInboxes(userCtx, req) - require.Error(t, err) - require.Contains(t, err.Error(), "cannot access inboxes") - }) - - t.Run("ListInboxes host can access other users' inboxes", func(t *testing.T) { - ts := NewTestService(t) - defer ts.Cleanup() - - // Create a host user and a regular user - hostUser, err := ts.CreateHostUser(ctx, "hostuser") - require.NoError(t, err) - regularUser, err := ts.CreateRegularUser(ctx, "regularuser") - require.NoError(t, err) - - // Create an inbox for the regular user - const systemBotID int32 = 0 - _, err = ts.Store.CreateInbox(ctx, &store.Inbox{ - SenderID: systemBotID, - ReceiverID: regularUser.ID, - Status: store.UNREAD, - Message: &storepb.InboxMessage{ - Type: storepb.InboxMessage_MEMO_COMMENT, - }, - }) - require.NoError(t, err) - - // Set host user context and try to list regular user's inboxes - hostCtx := ts.CreateUserContext(ctx, hostUser.ID) - - req := &v1pb.ListInboxesRequest{ - Parent: fmt.Sprintf("users/%d", regularUser.ID), - } - - resp, err := ts.Service.ListInboxes(hostCtx, req) - require.NoError(t, err) - require.NotNil(t, resp) - require.Equal(t, 1, len(resp.Inboxes)) - }) - - t.Run("ListInboxes invalid parent format", func(t *testing.T) { - ts := NewTestService(t) - defer ts.Cleanup() - - // Create a user - user, err := ts.CreateRegularUser(ctx, "testuser") - require.NoError(t, err) - - // Set user context - userCtx := ts.CreateUserContext(ctx, user.ID) - - req := &v1pb.ListInboxesRequest{ - Parent: "invalid-parent-format", - } - - _, err = ts.Service.ListInboxes(userCtx, req) - require.Error(t, err) - require.Contains(t, err.Error(), "invalid parent name") - }) - - t.Run("ListInboxes unauthenticated", func(t *testing.T) { - ts := NewTestService(t) - defer ts.Cleanup() - - req := &v1pb.ListInboxesRequest{ - Parent: "users/1", - } - - _, err := ts.Service.ListInboxes(ctx, req) - require.Error(t, err) - require.Contains(t, err.Error(), "user not authenticated") - }) -} - -func TestUpdateInbox(t *testing.T) { - ctx := context.Background() - - t.Run("UpdateInbox success", func(t *testing.T) { - ts := NewTestService(t) - defer ts.Cleanup() - - // Create a user - user, err := ts.CreateRegularUser(ctx, "testuser") - require.NoError(t, err) - - // Create an inbox entry - const systemBotID int32 = 0 - inbox, err := ts.Store.CreateInbox(ctx, &store.Inbox{ - SenderID: systemBotID, - ReceiverID: user.ID, - Status: store.UNREAD, - Message: &storepb.InboxMessage{ - Type: storepb.InboxMessage_MEMO_COMMENT, - }, - }) - require.NoError(t, err) - - // Set user context - userCtx := ts.CreateUserContext(ctx, user.ID) - - // Update inbox status - req := &v1pb.UpdateInboxRequest{ - Inbox: &v1pb.Inbox{ - Name: fmt.Sprintf("inboxes/%d", inbox.ID), - Status: v1pb.Inbox_ARCHIVED, - }, - UpdateMask: &fieldmaskpb.FieldMask{ - Paths: []string{"status"}, - }, - } - - resp, err := ts.Service.UpdateInbox(userCtx, req) - require.NoError(t, err) - require.NotNil(t, resp) - require.Equal(t, v1pb.Inbox_ARCHIVED, resp.Status) - }) - - t.Run("UpdateInbox permission denied for different user", func(t *testing.T) { - ts := NewTestService(t) - defer ts.Cleanup() - - // Create two users - user1, err := ts.CreateRegularUser(ctx, "user1") - require.NoError(t, err) - user2, err := ts.CreateRegularUser(ctx, "user2") - require.NoError(t, err) - - // Create an inbox entry for user2 - const systemBotID int32 = 0 - inbox, err := ts.Store.CreateInbox(ctx, &store.Inbox{ - SenderID: systemBotID, - ReceiverID: user2.ID, - Status: store.UNREAD, - Message: &storepb.InboxMessage{ - Type: storepb.InboxMessage_MEMO_COMMENT, - }, - }) - require.NoError(t, err) - - // Set user1 context but try to update user2's inbox - userCtx := ts.CreateUserContext(ctx, user1.ID) - - req := &v1pb.UpdateInboxRequest{ - Inbox: &v1pb.Inbox{ - Name: fmt.Sprintf("inboxes/%d", inbox.ID), - Status: v1pb.Inbox_ARCHIVED, - }, - UpdateMask: &fieldmaskpb.FieldMask{ - Paths: []string{"status"}, - }, - } - - _, err = ts.Service.UpdateInbox(userCtx, req) - require.Error(t, err) - require.Contains(t, err.Error(), "cannot update inbox") - }) - - t.Run("UpdateInbox missing update mask", func(t *testing.T) { - ts := NewTestService(t) - defer ts.Cleanup() - - // Create a user - user, err := ts.CreateRegularUser(ctx, "testuser") - require.NoError(t, err) - - // Set user context - userCtx := ts.CreateUserContext(ctx, user.ID) - - req := &v1pb.UpdateInboxRequest{ - Inbox: &v1pb.Inbox{ - Name: "inboxes/1", - Status: v1pb.Inbox_ARCHIVED, - }, - } - - _, err = ts.Service.UpdateInbox(userCtx, req) - require.Error(t, err) - require.Contains(t, err.Error(), "update mask is required") - }) - - t.Run("UpdateInbox invalid name format", func(t *testing.T) { - ts := NewTestService(t) - defer ts.Cleanup() - - // Create a user - user, err := ts.CreateRegularUser(ctx, "testuser") - require.NoError(t, err) - - // Set user context - userCtx := ts.CreateUserContext(ctx, user.ID) - - req := &v1pb.UpdateInboxRequest{ - Inbox: &v1pb.Inbox{ - Name: "invalid-inbox-name", - Status: v1pb.Inbox_ARCHIVED, - }, - UpdateMask: &fieldmaskpb.FieldMask{ - Paths: []string{"status"}, - }, - } - - _, err = ts.Service.UpdateInbox(userCtx, req) - require.Error(t, err) - require.Contains(t, err.Error(), "invalid inbox name") - }) - - t.Run("UpdateInbox not found", func(t *testing.T) { - ts := NewTestService(t) - defer ts.Cleanup() - - // Create a user - user, err := ts.CreateRegularUser(ctx, "testuser") - require.NoError(t, err) - - // Set user context - userCtx := ts.CreateUserContext(ctx, user.ID) - - req := &v1pb.UpdateInboxRequest{ - Inbox: &v1pb.Inbox{ - Name: "inboxes/99999", // Non-existent inbox - Status: v1pb.Inbox_ARCHIVED, - }, - UpdateMask: &fieldmaskpb.FieldMask{ - Paths: []string{"status"}, - }, - } - - _, err = ts.Service.UpdateInbox(userCtx, req) - require.Error(t, err) - st, ok := status.FromError(err) - require.True(t, ok) - require.Equal(t, codes.NotFound, st.Code()) - }) - - t.Run("UpdateInbox unsupported field", func(t *testing.T) { - ts := NewTestService(t) - defer ts.Cleanup() - - // Create a user - user, err := ts.CreateRegularUser(ctx, "testuser") - require.NoError(t, err) - - // Create an inbox entry - const systemBotID int32 = 0 - inbox, err := ts.Store.CreateInbox(ctx, &store.Inbox{ - SenderID: systemBotID, - ReceiverID: user.ID, - Status: store.UNREAD, - Message: &storepb.InboxMessage{ - Type: storepb.InboxMessage_MEMO_COMMENT, - }, - }) - require.NoError(t, err) - - // Set user context - userCtx := ts.CreateUserContext(ctx, user.ID) - - req := &v1pb.UpdateInboxRequest{ - Inbox: &v1pb.Inbox{ - Name: fmt.Sprintf("inboxes/%d", inbox.ID), - Status: v1pb.Inbox_ARCHIVED, - }, - UpdateMask: &fieldmaskpb.FieldMask{ - Paths: []string{"unsupported_field"}, - }, - } - - _, err = ts.Service.UpdateInbox(userCtx, req) - require.Error(t, err) - require.Contains(t, err.Error(), "unsupported field") - }) -} - -func TestDeleteInbox(t *testing.T) { - ctx := context.Background() - - t.Run("DeleteInbox success", func(t *testing.T) { - ts := NewTestService(t) - defer ts.Cleanup() - - // Create a user - user, err := ts.CreateRegularUser(ctx, "testuser") - require.NoError(t, err) - - // Create an inbox entry - const systemBotID int32 = 0 - inbox, err := ts.Store.CreateInbox(ctx, &store.Inbox{ - SenderID: systemBotID, - ReceiverID: user.ID, - Status: store.UNREAD, - Message: &storepb.InboxMessage{ - Type: storepb.InboxMessage_MEMO_COMMENT, - }, - }) - require.NoError(t, err) - - // Set user context - userCtx := ts.CreateUserContext(ctx, user.ID) - - // Delete inbox - req := &v1pb.DeleteInboxRequest{ - Name: fmt.Sprintf("inboxes/%d", inbox.ID), - } - - _, err = ts.Service.DeleteInbox(userCtx, req) - require.NoError(t, err) - - // Verify inbox is deleted - inboxes, err := ts.Store.ListInboxes(ctx, &store.FindInbox{ - ReceiverID: &user.ID, - }) - require.NoError(t, err) - require.Equal(t, 0, len(inboxes)) - }) - - t.Run("DeleteInbox permission denied for different user", func(t *testing.T) { - ts := NewTestService(t) - defer ts.Cleanup() - - // Create two users - user1, err := ts.CreateRegularUser(ctx, "user1") - require.NoError(t, err) - user2, err := ts.CreateRegularUser(ctx, "user2") - require.NoError(t, err) - - // Create an inbox entry for user2 - const systemBotID int32 = 0 - inbox, err := ts.Store.CreateInbox(ctx, &store.Inbox{ - SenderID: systemBotID, - ReceiverID: user2.ID, - Status: store.UNREAD, - Message: &storepb.InboxMessage{ - Type: storepb.InboxMessage_MEMO_COMMENT, - }, - }) - require.NoError(t, err) - - // Set user1 context but try to delete user2's inbox - userCtx := ts.CreateUserContext(ctx, user1.ID) - - req := &v1pb.DeleteInboxRequest{ - Name: fmt.Sprintf("inboxes/%d", inbox.ID), - } - - _, err = ts.Service.DeleteInbox(userCtx, req) - require.Error(t, err) - require.Contains(t, err.Error(), "cannot delete inbox") - }) - - t.Run("DeleteInbox invalid name format", func(t *testing.T) { - ts := NewTestService(t) - defer ts.Cleanup() - - // Create a user - user, err := ts.CreateRegularUser(ctx, "testuser") - require.NoError(t, err) - - // Set user context - userCtx := ts.CreateUserContext(ctx, user.ID) - - req := &v1pb.DeleteInboxRequest{ - Name: "invalid-inbox-name", - } - - _, err = ts.Service.DeleteInbox(userCtx, req) - require.Error(t, err) - require.Contains(t, err.Error(), "invalid inbox name") - }) - - t.Run("DeleteInbox not found", func(t *testing.T) { - ts := NewTestService(t) - defer ts.Cleanup() - - // Create a user - user, err := ts.CreateRegularUser(ctx, "testuser") - require.NoError(t, err) - - // Set user context - userCtx := ts.CreateUserContext(ctx, user.ID) - - req := &v1pb.DeleteInboxRequest{ - Name: "inboxes/99999", // Non-existent inbox - } - - _, err = ts.Service.DeleteInbox(userCtx, req) - require.Error(t, err) - st, ok := status.FromError(err) - require.True(t, ok) - require.Equal(t, codes.NotFound, st.Code()) - }) -} - -func TestInboxCRUDComplete(t *testing.T) { - ctx := context.Background() - - t.Run("Complete CRUD lifecycle", func(t *testing.T) { - ts := NewTestService(t) - defer ts.Cleanup() - - // Create a user - user, err := ts.CreateRegularUser(ctx, "testuser") - require.NoError(t, err) - - // Create an inbox entry directly in store - const systemBotID int32 = 0 - inbox, err := ts.Store.CreateInbox(ctx, &store.Inbox{ - SenderID: systemBotID, - ReceiverID: user.ID, - Status: store.UNREAD, - Message: &storepb.InboxMessage{ - Type: storepb.InboxMessage_MEMO_COMMENT, - }, - }) - require.NoError(t, err) - - // Set user context - userCtx := ts.CreateUserContext(ctx, user.ID) - - // 1. List inboxes - should have 1 - listReq := &v1pb.ListInboxesRequest{ - Parent: fmt.Sprintf("users/%d", user.ID), - } - listResp, err := ts.Service.ListInboxes(userCtx, listReq) - require.NoError(t, err) - require.Equal(t, 1, len(listResp.Inboxes)) - require.Equal(t, v1pb.Inbox_UNREAD, listResp.Inboxes[0].Status) - - // 2. Update inbox status to ARCHIVED - updateReq := &v1pb.UpdateInboxRequest{ - Inbox: &v1pb.Inbox{ - Name: fmt.Sprintf("inboxes/%d", inbox.ID), - Status: v1pb.Inbox_ARCHIVED, - }, - UpdateMask: &fieldmaskpb.FieldMask{ - Paths: []string{"status"}, - }, - } - updateResp, err := ts.Service.UpdateInbox(userCtx, updateReq) - require.NoError(t, err) - require.Equal(t, v1pb.Inbox_ARCHIVED, updateResp.Status) - - // 3. List inboxes again - should still have 1 but ARCHIVED - listResp, err = ts.Service.ListInboxes(userCtx, listReq) - require.NoError(t, err) - require.Equal(t, 1, len(listResp.Inboxes)) - require.Equal(t, v1pb.Inbox_ARCHIVED, listResp.Inboxes[0].Status) - - // 4. Delete inbox - deleteReq := &v1pb.DeleteInboxRequest{ - Name: fmt.Sprintf("inboxes/%d", inbox.ID), - } - _, err = ts.Service.DeleteInbox(userCtx, deleteReq) - require.NoError(t, err) - - // 5. List inboxes - should be empty - listResp, err = ts.Service.ListInboxes(userCtx, listReq) - require.NoError(t, err) - require.Equal(t, 0, len(listResp.Inboxes)) - require.Equal(t, int32(0), listResp.TotalSize) - }) -} diff --git a/server/router/api/v1/user_service.go b/server/router/api/v1/user_service.go index 1f926df84..84649926d 100644 --- a/server/router/api/v1/user_service.go +++ b/server/router/api/v1/user_service.go @@ -1552,3 +1552,201 @@ func extractUsernameFromComparison(left, right ast.Expr) (string, bool) { return str, true } + +// ListUserNotifications lists all notifications for a user. +// Notifications are backed by the inbox storage layer and represent activities +// that require user attention (e.g., memo comments). +func (s *APIV1Service) ListUserNotifications(ctx context.Context, request *v1pb.ListUserNotificationsRequest) (*v1pb.ListUserNotificationsResponse, error) { + userID, err := ExtractUserIDFromName(request.Parent) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid user name: %v", err) + } + + // Verify the requesting user has permission to view these notifications + currentUser, err := s.GetCurrentUser(ctx) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err) + } + if currentUser.ID != userID { + return nil, status.Errorf(codes.PermissionDenied, "permission denied") + } + + // Fetch inbox items from storage + inboxes, err := s.Store.ListInboxes(ctx, &store.FindInbox{ + ReceiverID: &userID, + }) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to list inboxes: %v", err) + } + + // Convert storage layer inboxes to API notifications + notifications := []*v1pb.UserNotification{} + for _, inbox := range inboxes { + notification, err := s.convertInboxToUserNotification(ctx, inbox) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to convert inbox: %v", err) + } + notifications = append(notifications, notification) + } + + return &v1pb.ListUserNotificationsResponse{ + Notifications: notifications, + }, nil +} + +// UpdateUserNotification updates a notification's status (e.g., marking as read/archived). +// Only the notification owner can update their notifications. +func (s *APIV1Service) UpdateUserNotification(ctx context.Context, request *v1pb.UpdateUserNotificationRequest) (*v1pb.UserNotification, error) { + if request.Notification == nil { + return nil, status.Errorf(codes.InvalidArgument, "notification is required") + } + + notificationID, err := ExtractNotificationIDFromName(request.Notification.Name) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid notification name: %v", err) + } + + currentUser, err := s.GetCurrentUser(ctx) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err) + } + + // Verify ownership before updating + inboxes, err := s.Store.ListInboxes(ctx, &store.FindInbox{ + ID: ¬ificationID, + }) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get inbox: %v", err) + } + if len(inboxes) == 0 { + return nil, status.Errorf(codes.NotFound, "notification not found") + } + inbox := inboxes[0] + if inbox.ReceiverID != currentUser.ID { + return nil, status.Errorf(codes.PermissionDenied, "permission denied") + } + + // Build update request based on field mask + update := &store.UpdateInbox{ + ID: notificationID, + } + + for _, path := range request.UpdateMask.Paths { + switch path { + case "status": + // Convert API status enum to storage enum + var inboxStatus store.InboxStatus + switch request.Notification.Status { + case v1pb.UserNotification_UNREAD: + inboxStatus = store.UNREAD + case v1pb.UserNotification_ARCHIVED: + inboxStatus = store.ARCHIVED + default: + return nil, status.Errorf(codes.InvalidArgument, "invalid status") + } + update.Status = inboxStatus + } + } + + updatedInbox, err := s.Store.UpdateInbox(ctx, update) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to update inbox: %v", err) + } + + notification, err := s.convertInboxToUserNotification(ctx, updatedInbox) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to convert inbox: %v", err) + } + + return notification, nil +} + +// DeleteUserNotification permanently deletes a notification. +// Only the notification owner can delete their notifications. +func (s *APIV1Service) DeleteUserNotification(ctx context.Context, request *v1pb.DeleteUserNotificationRequest) (*emptypb.Empty, error) { + notificationID, err := ExtractNotificationIDFromName(request.Name) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid notification name: %v", err) + } + + currentUser, err := s.GetCurrentUser(ctx) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err) + } + + // Verify ownership before deletion + inboxes, err := s.Store.ListInboxes(ctx, &store.FindInbox{ + ID: ¬ificationID, + }) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get inbox: %v", err) + } + if len(inboxes) == 0 { + return nil, status.Errorf(codes.NotFound, "notification not found") + } + inbox := inboxes[0] + if inbox.ReceiverID != currentUser.ID { + return nil, status.Errorf(codes.PermissionDenied, "permission denied") + } + + if err := s.Store.DeleteInbox(ctx, &store.DeleteInbox{ + ID: notificationID, + }); err != nil { + return nil, status.Errorf(codes.Internal, "failed to delete inbox: %v", err) + } + + return &emptypb.Empty{}, nil +} + +// convertInboxToUserNotification converts a storage-layer inbox to an API notification. +// This handles the mapping between the internal inbox representation and the public API. +func (s *APIV1Service) convertInboxToUserNotification(ctx context.Context, inbox *store.Inbox) (*v1pb.UserNotification, error) { + notification := &v1pb.UserNotification{ + Name: fmt.Sprintf("users/%d/notifications/%d", inbox.ReceiverID, inbox.ID), + Sender: fmt.Sprintf("%s%d", UserNamePrefix, inbox.SenderID), + CreateTime: timestamppb.New(time.Unix(inbox.CreatedTs, 0)), + } + + // Convert status from storage enum to API enum + switch inbox.Status { + case store.UNREAD: + notification.Status = v1pb.UserNotification_UNREAD + case store.ARCHIVED: + notification.Status = v1pb.UserNotification_ARCHIVED + default: + notification.Status = v1pb.UserNotification_STATUS_UNSPECIFIED + } + + // Extract notification type and activity ID from inbox message + if inbox.Message != nil { + switch inbox.Message.Type { + case storepb.InboxMessage_MEMO_COMMENT: + notification.Type = v1pb.UserNotification_MEMO_COMMENT + default: + notification.Type = v1pb.UserNotification_TYPE_UNSPECIFIED + } + + if inbox.Message.ActivityId != nil { + notification.ActivityId = inbox.Message.ActivityId + } + } + + return notification, nil +} + +// ExtractNotificationIDFromName extracts the notification ID from a resource name. +// Expected format: users/{user_id}/notifications/{notification_id} +func ExtractNotificationIDFromName(name string) (int32, error) { + pattern := regexp.MustCompile(`^users/(\d+)/notifications/(\d+)$`) + matches := pattern.FindStringSubmatch(name) + if len(matches) != 3 { + return 0, errors.Errorf("invalid notification name: %s", name) + } + + id, err := strconv.Atoi(matches[2]) + if err != nil { + return 0, errors.Errorf("invalid notification id: %s", matches[2]) + } + + return int32(id), nil +} diff --git a/server/router/api/v1/v1.go b/server/router/api/v1/v1.go index a8b7ecc57..e3dd5d1e8 100644 --- a/server/router/api/v1/v1.go +++ b/server/router/api/v1/v1.go @@ -29,7 +29,6 @@ type APIV1Service struct { v1pb.UnimplementedMemoServiceServer v1pb.UnimplementedAttachmentServiceServer v1pb.UnimplementedShortcutServiceServer - v1pb.UnimplementedInboxServiceServer v1pb.UnimplementedActivityServiceServer v1pb.UnimplementedIdentityProviderServiceServer @@ -60,7 +59,6 @@ func NewAPIV1Service(secret string, profile *profile.Profile, store *store.Store v1pb.RegisterMemoServiceServer(grpcServer, apiv1Service) v1pb.RegisterAttachmentServiceServer(grpcServer, apiv1Service) v1pb.RegisterShortcutServiceServer(grpcServer, apiv1Service) - v1pb.RegisterInboxServiceServer(grpcServer, apiv1Service) v1pb.RegisterActivityServiceServer(grpcServer, apiv1Service) v1pb.RegisterIdentityProviderServiceServer(grpcServer, apiv1Service) reflection.Register(grpcServer) @@ -107,9 +105,6 @@ func (s *APIV1Service) RegisterGateway(ctx context.Context, echoServer *echo.Ech if err := v1pb.RegisterShortcutServiceHandler(ctx, gwMux, conn); err != nil { return err } - if err := v1pb.RegisterInboxServiceHandler(ctx, gwMux, conn); err != nil { - return err - } if err := v1pb.RegisterActivityServiceHandler(ctx, gwMux, conn); err != nil { return err } diff --git a/store/inbox.go b/store/inbox.go index e68ce2296..2df3c19d1 100644 --- a/store/inbox.go +++ b/store/inbox.go @@ -6,11 +6,13 @@ import ( storepb "github.com/usememos/memos/proto/gen/store" ) -// InboxStatus is the status for an inbox. +// InboxStatus represents the status of an inbox notification. type InboxStatus string const ( - UNREAD InboxStatus = "UNREAD" + // UNREAD indicates the notification has not been read by the user. + UNREAD InboxStatus = "UNREAD" + // ARCHIVED indicates the notification has been archived/dismissed by the user. ARCHIVED InboxStatus = "ARCHIVED" ) @@ -18,20 +20,24 @@ func (s InboxStatus) String() string { return string(s) } +// Inbox represents a notification in a user's inbox. +// It connects activities to users who should be notified. type Inbox struct { ID int32 CreatedTs int64 - SenderID int32 - ReceiverID int32 - Status InboxStatus - Message *storepb.InboxMessage + SenderID int32 // The user who triggered the notification + ReceiverID int32 // The user who receives the notification + Status InboxStatus // Current status (unread/archived) + Message *storepb.InboxMessage // The notification message content } +// UpdateInbox contains fields that can be updated for an inbox item. type UpdateInbox struct { ID int32 Status InboxStatus } +// FindInbox specifies filter criteria for querying inbox items. type FindInbox struct { ID *int32 SenderID *int32 @@ -43,22 +49,27 @@ type FindInbox struct { Offset *int } +// DeleteInbox specifies which inbox item to delete. type DeleteInbox struct { ID int32 } +// CreateInbox creates a new inbox notification. func (s *Store) CreateInbox(ctx context.Context, create *Inbox) (*Inbox, error) { return s.driver.CreateInbox(ctx, create) } +// ListInboxes retrieves inbox items matching the filter criteria. func (s *Store) ListInboxes(ctx context.Context, find *FindInbox) ([]*Inbox, error) { return s.driver.ListInboxes(ctx, find) } +// UpdateInbox updates an existing inbox item. func (s *Store) UpdateInbox(ctx context.Context, update *UpdateInbox) (*Inbox, error) { return s.driver.UpdateInbox(ctx, update) } +// DeleteInbox permanently removes an inbox item. func (s *Store) DeleteInbox(ctx context.Context, delete *DeleteInbox) error { return s.driver.DeleteInbox(ctx, delete) } diff --git a/web/src/components/Inbox/MemoCommentMessage.tsx b/web/src/components/Inbox/MemoCommentMessage.tsx index ef132ce8f..b18bf9a20 100644 --- a/web/src/components/Inbox/MemoCommentMessage.tsx +++ b/web/src/components/Inbox/MemoCommentMessage.tsx @@ -1,39 +1,39 @@ -import { InboxIcon, LoaderIcon, MessageCircleIcon, TrashIcon } from "lucide-react"; +import { CheckIcon, MessageCircleIcon, TrashIcon, XIcon } from "lucide-react"; import { observer } from "mobx-react-lite"; import { useState } from "react"; import toast from "react-hot-toast"; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; +import UserAvatar from "@/components/UserAvatar"; import { activityServiceClient } from "@/grpcweb"; import useAsyncEffect from "@/hooks/useAsyncEffect"; import useNavigateTo from "@/hooks/useNavigateTo"; import { cn } from "@/lib/utils"; import { memoStore, userStore } from "@/store"; import { activityNamePrefix } from "@/store/common"; -import { Inbox, Inbox_Status } from "@/types/proto/api/v1/inbox_service"; import { Memo } from "@/types/proto/api/v1/memo_service"; -import { User } from "@/types/proto/api/v1/user_service"; +import { User, UserNotification, UserNotification_Status } from "@/types/proto/api/v1/user_service"; import { useTranslate } from "@/utils/i18n"; interface Props { - inbox: Inbox; + notification: UserNotification; } -const MemoCommentMessage = observer(({ inbox }: Props) => { +const MemoCommentMessage = observer(({ notification }: Props) => { const t = useTranslate(); const navigateTo = useNavigateTo(); const [relatedMemo, setRelatedMemo] = useState(undefined); + const [commentMemo, setCommentMemo] = useState(undefined); const [sender, setSender] = useState(undefined); const [initialized, setInitialized] = useState(false); const [hasError, setHasError] = useState(false); useAsyncEffect(async () => { - if (!inbox.activityId) { + if (!notification.activityId) { return; } try { const activity = await activityServiceClient.getActivity({ - name: `${activityNamePrefix}${inbox.activityId}`, + name: `${activityNamePrefix}${notification.activityId}`, }); if (activity.payload?.memoComment) { @@ -42,7 +42,14 @@ const MemoCommentMessage = observer(({ inbox }: Props) => { skipStore: true, }); setRelatedMemo(memo); - const sender = await userStore.getOrFetchUserByName(inbox.sender); + + // Fetch the comment memo + const comment = await memoStore.getOrFetchMemoByName(memoCommentPayload.memo, { + skipStore: true, + }); + setCommentMemo(comment); + + const sender = await userStore.getOrFetchUserByName(notification.sender); setSender(sender); setInitialized(true); } @@ -51,7 +58,7 @@ const MemoCommentMessage = observer(({ inbox }: Props) => { setHasError(true); return; } - }, [inbox.activityId]); + }, [notification.activityId]); const handleNavigateToMemo = async () => { if (!relatedMemo) { @@ -59,16 +66,16 @@ const MemoCommentMessage = observer(({ inbox }: Props) => { } navigateTo(`/${relatedMemo.name}`); - if (inbox.status === Inbox_Status.UNREAD) { + if (notification.status === UserNotification_Status.UNREAD) { handleArchiveMessage(true); } }; const handleArchiveMessage = async (silence = false) => { - await userStore.updateInbox( + await userStore.updateNotification( { - name: inbox.name, - status: Inbox_Status.ARCHIVED, + name: notification.name, + status: UserNotification_Status.ARCHIVED, }, ["status"], ); @@ -78,104 +85,120 @@ const MemoCommentMessage = observer(({ inbox }: Props) => { }; const handleDeleteMessage = async () => { - await userStore.deleteInbox(inbox.name); + await userStore.deleteNotification(notification.name); toast.success(t("message.deleted-successfully")); }; - const deleteButton = () => ( - <> -
- - - - handleDeleteMessage()} - /> - - -

{t("common.delete")}

-
-
-
+ if (!initialized && !hasError) { + return ( +
+
+
+
+
+
+
+
- - ); + ); + } - const archiveButton = () => ( - <> -
- - - - handleArchiveMessage()} - /> - - -

{t("common.archive")}

-
-
-
+ if (hasError) { + return ( +
+
+
+
+ +
+ {t("inbox.failed-to-load")} +
+ +
- - ); + ); + } + + const isUnread = notification.status === UserNotification_Status.UNREAD; return ( -
-
- - - - - - -

Comment

-
-
-
-
-
- {initialized ? ( - <> -
- {inbox.createTime?.toLocaleString()} - {inbox.status === Inbox_Status.UNREAD ? archiveButton() : deleteButton()} +
+ {/* Unread indicator bar */} + {isUnread &&
} + +
+ {/* Avatar & Icon */} +
+ +
+ +
+
+ + {/* Content */} +
+ {/* Header */} +
+
+ {sender?.displayName || sender?.username} + commented on your memo + + ยท {notification.createTime?.toLocaleDateString([], { month: "short", day: "numeric" })} at{" "} + {notification.createTime?.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })} + +
+
+ {isUnread ? ( + + ) : ( + + )}
-

+ + {/* Comment Preview */} + {commentMemo && ( +

- {t("inbox.memo-comment", { - user: sender?.displayName || sender?.username, - memo: relatedMemo?.name, - interpolation: { escapeValue: false }, - })} -

- - ) : hasError ? ( -
- {t("inbox.failed-to-load")} - {deleteButton()} -
- ) : ( -
- -
- )} +
+ +

+ {commentMemo.content || Empty comment} +

+
+
+ )} +
); diff --git a/web/src/components/Navigation.tsx b/web/src/components/Navigation.tsx index d715ac7d8..a0de9c719 100644 --- a/web/src/components/Navigation.tsx +++ b/web/src/components/Navigation.tsx @@ -1,4 +1,4 @@ -import { EarthIcon, LibraryIcon, PaperclipIcon, UserCircleIcon } from "lucide-react"; +import { BellIcon, EarthIcon, LibraryIcon, PaperclipIcon, UserCircleIcon } from "lucide-react"; import { observer } from "mobx-react-lite"; import { useEffect } from "react"; import { NavLink } from "react-router-dom"; @@ -33,7 +33,7 @@ const Navigation = observer((props: Props) => { return; } - userStore.fetchInboxes(); + userStore.fetchNotifications(); }, []); const homeNavLink: NavLinkItem = { @@ -54,6 +54,22 @@ const Navigation = observer((props: Props) => { title: t("common.attachments"), icon: , }; + const unreadCount = userStore.state.notifications.filter((n) => n.status === "UNREAD").length; + const inboxNavLink: NavLinkItem = { + id: "header-inbox", + path: Routes.INBOX, + title: t("common.inbox"), + icon: ( +
+ + {unreadCount > 0 && ( + + {unreadCount > 99 ? "99+" : unreadCount} + + )} +
+ ), + }; const signInNavLink: NavLinkItem = { id: "header-auth", path: Routes.AUTH, @@ -61,7 +77,9 @@ const Navigation = observer((props: Props) => { icon: , }; - const navLinks: NavLinkItem[] = currentUser ? [homeNavLink, exploreNavLink, attachmentsNavLink] : [exploreNavLink, signInNavLink]; + const navLinks: NavLinkItem[] = currentUser + ? [homeNavLink, exploreNavLink, attachmentsNavLink, inboxNavLink] + : [exploreNavLink, signInNavLink]; return (
diff --git a/web/src/components/UserMenu.tsx b/web/src/components/UserMenu.tsx index 6345a0c97..36d217569 100644 --- a/web/src/components/UserMenu.tsx +++ b/web/src/components/UserMenu.tsx @@ -1,14 +1,4 @@ -import { - ArchiveIcon, - LogOutIcon, - User2Icon, - SquareUserIcon, - SettingsIcon, - BellIcon, - GlobeIcon, - PaletteIcon, - CheckIcon, -} from "lucide-react"; +import { ArchiveIcon, LogOutIcon, User2Icon, SquareUserIcon, SettingsIcon, GlobeIcon, PaletteIcon, CheckIcon } from "lucide-react"; import { observer } from "mobx-react-lite"; import { authServiceClient } from "@/grpcweb"; import useCurrentUser from "@/hooks/useCurrentUser"; @@ -99,10 +89,6 @@ const UserMenu = observer((props: Props) => { {t("common.archived")} - navigateTo(Routes.INBOX)}> - - {t("common.inbox")} - diff --git a/web/src/grpcweb.ts b/web/src/grpcweb.ts index affc9050d..507b735ad 100644 --- a/web/src/grpcweb.ts +++ b/web/src/grpcweb.ts @@ -3,7 +3,6 @@ import { ActivityServiceDefinition } from "./types/proto/api/v1/activity_service import { AttachmentServiceDefinition } from "./types/proto/api/v1/attachment_service"; import { AuthServiceDefinition } from "./types/proto/api/v1/auth_service"; import { IdentityProviderServiceDefinition } from "./types/proto/api/v1/idp_service"; -import { InboxServiceDefinition } from "./types/proto/api/v1/inbox_service"; import { MemoServiceDefinition } from "./types/proto/api/v1/memo_service"; import { ShortcutServiceDefinition } from "./types/proto/api/v1/shortcut_service"; import { UserServiceDefinition } from "./types/proto/api/v1/user_service"; @@ -30,8 +29,6 @@ export const attachmentServiceClient = clientFactory.create(AttachmentServiceDef export const shortcutServiceClient = clientFactory.create(ShortcutServiceDefinition, channel); -export const inboxServiceClient = clientFactory.create(InboxServiceDefinition, channel); - export const activityServiceClient = clientFactory.create(ActivityServiceDefinition, channel); export const identityProviderServiceClient = clientFactory.create(IdentityProviderServiceDefinition, channel); diff --git a/web/src/locales/en.json b/web/src/locales/en.json index 0b8f6bf92..17c0ec1ef 100644 --- a/web/src/locales/en.json +++ b/web/src/locales/en.json @@ -18,6 +18,7 @@ "about": "About", "add": "Add", "admin": "Admin", + "all": "All", "archive": "Archive", "archived": "Archived", "attachments": "Attachments", @@ -125,8 +126,10 @@ }, "inbox": { "memo-comment": "{{user}} has a comment on your {{memo}}.", - "version-update": "New version {{version}} is available now!", - "failed-to-load": "Failed to load inbox item" + "failed-to-load": "Failed to load inbox item", + "unread": "Unread", + "no-unread": "No unread notifications", + "no-archived": "No archived notifications" }, "markdown": { "checkbox": "Checkbox", diff --git a/web/src/pages/Inboxes.tsx b/web/src/pages/Inboxes.tsx index aca73663b..169897101 100644 --- a/web/src/pages/Inboxes.tsx +++ b/web/src/pages/Inboxes.tsx @@ -1,63 +1,126 @@ import { sortBy } from "lodash-es"; -import { BellIcon } from "lucide-react"; +import { ArchiveIcon, BellIcon, InboxIcon } from "lucide-react"; import { observer } from "mobx-react-lite"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import Empty from "@/components/Empty"; import MemoCommentMessage from "@/components/Inbox/MemoCommentMessage"; import MobileHeader from "@/components/MobileHeader"; import useResponsiveWidth from "@/hooks/useResponsiveWidth"; +import { cn } from "@/lib/utils"; import { userStore } from "@/store"; -import { Inbox, Inbox_Status, Inbox_Type } from "@/types/proto/api/v1/inbox_service"; +import { UserNotification, UserNotification_Status, UserNotification_Type } from "@/types/proto/api/v1/user_service"; import { useTranslate } from "@/utils/i18n"; const Inboxes = observer(() => { const t = useTranslate(); const { md } = useResponsiveWidth(); + const [filter, setFilter] = useState<"all" | "unread" | "archived">("all"); - const inboxes = sortBy(userStore.state.inboxes, (inbox: Inbox) => { - if (inbox.status === Inbox_Status.UNREAD) return 0; - if (inbox.status === Inbox_Status.ARCHIVED) return 1; - return 2; + const allNotifications = sortBy(userStore.state.notifications, (notification: UserNotification) => { + return -(notification.createTime?.getTime() || 0); }); - const fetchInboxes = async () => { + const notifications = allNotifications.filter((notification) => { + if (filter === "unread") return notification.status === UserNotification_Status.UNREAD; + if (filter === "archived") return notification.status === UserNotification_Status.ARCHIVED; + return true; + }); + + const unreadCount = allNotifications.filter((n) => n.status === UserNotification_Status.UNREAD).length; + const archivedCount = allNotifications.filter((n) => n.status === UserNotification_Status.ARCHIVED).length; + + const fetchNotifications = async () => { try { - await userStore.fetchInboxes(); + await userStore.fetchNotifications(); } catch (error) { - console.error("Failed to fetch inboxes:", error); + console.error("Failed to fetch notifications:", error); } }; useEffect(() => { - fetchInboxes(); + fetchNotifications(); }, []); return (
{!md && }
-
-
-

- - {t("common.inbox")} -

+
+ {/* Header */} +
+
+
+ +

{t("common.inbox")}

+ {unreadCount > 0 && ( + + {unreadCount} + + )} +
+
-
- {inboxes.length === 0 && ( -
+ + {/* Filter Tabs */} +
+
+ + + +
+
+ + {/* Notifications List */} +
+ {notifications.length === 0 ? ( +
-

{t("message.no-data")}

+

+ {filter === "unread" ? t("inbox.no-unread") : filter === "archived" ? t("inbox.no-archived") : t("message.no-data")} +

+
+ ) : ( +
+ {notifications.map((notification: UserNotification) => { + if (notification.type === UserNotification_Type.MEMO_COMMENT) { + return ; + } + return null; + })}
)} -
- {inboxes.map((inbox: Inbox) => { - if (inbox.type === Inbox_Type.MEMO_COMMENT) { - return ; - } - return undefined; - })} -
diff --git a/web/src/store/user.ts b/web/src/store/user.ts index 1e656fbaa..0526df42e 100644 --- a/web/src/store/user.ts +++ b/web/src/store/user.ts @@ -1,10 +1,10 @@ import { uniqueId } from "lodash-es"; import { makeAutoObservable, computed } from "mobx"; -import { authServiceClient, inboxServiceClient, userServiceClient, shortcutServiceClient } from "@/grpcweb"; -import { Inbox } from "@/types/proto/api/v1/inbox_service"; +import { authServiceClient, userServiceClient, shortcutServiceClient } from "@/grpcweb"; import { Shortcut } from "@/types/proto/api/v1/shortcut_service"; import { User, + UserNotification, UserSetting, UserSetting_Key, UserSetting_GeneralSetting, @@ -24,7 +24,7 @@ class LocalState { userAccessTokensSetting?: UserSetting_AccessTokensSetting; userWebhooksSetting?: UserSetting_WebhooksSetting; shortcuts: Shortcut[] = []; - inboxes: Inbox[] = []; + notifications: UserNotification[] = []; userMapByName: Record = {}; userStatsByName: Record = {}; @@ -218,40 +218,40 @@ const userStore = (() => { // Note: fetchShortcuts is now handled by fetchUserSettings // The shortcuts are extracted from the user shortcuts setting - const fetchInboxes = async () => { + const fetchNotifications = async () => { if (!state.currentUser) { throw new Error("No current user available"); } - const { inboxes } = await inboxServiceClient.listInboxes({ + const { notifications } = await userServiceClient.listUserNotifications({ parent: state.currentUser, }); state.setPartial({ - inboxes, + notifications, }); }; - const updateInbox = async (inbox: Partial, updateMask: string[]) => { - const updatedInbox = await inboxServiceClient.updateInbox({ - inbox, + const updateNotification = async (notification: Partial, updateMask: string[]) => { + const updatedNotification = await userServiceClient.updateUserNotification({ + notification, updateMask, }); state.setPartial({ - inboxes: state.inboxes.map((i) => { - if (i.name === updatedInbox.name) { - return updatedInbox; + notifications: state.notifications.map((n) => { + if (n.name === updatedNotification.name) { + return updatedNotification; } - return i; + return n; }), }); - return updatedInbox; + return updatedNotification; }; - const deleteInbox = async (name: string) => { - await inboxServiceClient.deleteInbox({ name }); + const deleteNotification = async (name: string) => { + await userServiceClient.deleteUserNotification({ name }); state.setPartial({ - inboxes: state.inboxes.filter((i) => i.name !== name), + notifications: state.notifications.filter((n) => n.name !== name), }); }; @@ -296,9 +296,9 @@ const userStore = (() => { updateUserGeneralSetting, getUserGeneralSetting, fetchUserSettings, - fetchInboxes, - updateInbox, - deleteInbox, + fetchNotifications, + updateNotification, + deleteNotification, fetchUserStats, setStatsStateId, }; diff --git a/web/src/types/proto/api/v1/activity_service.ts b/web/src/types/proto/api/v1/activity_service.ts index 56efe5163..cfdceac74 100644 --- a/web/src/types/proto/api/v1/activity_service.ts +++ b/web/src/types/proto/api/v1/activity_service.ts @@ -39,8 +39,6 @@ export enum Activity_Type { TYPE_UNSPECIFIED = "TYPE_UNSPECIFIED", /** MEMO_COMMENT - Memo comment activity. */ MEMO_COMMENT = "MEMO_COMMENT", - /** VERSION_UPDATE - Version update activity. */ - VERSION_UPDATE = "VERSION_UPDATE", UNRECOGNIZED = "UNRECOGNIZED", } @@ -52,9 +50,6 @@ export function activity_TypeFromJSON(object: any): Activity_Type { case 1: case "MEMO_COMMENT": return Activity_Type.MEMO_COMMENT; - case 2: - case "VERSION_UPDATE": - return Activity_Type.VERSION_UPDATE; case -1: case "UNRECOGNIZED": default: @@ -68,8 +63,6 @@ export function activity_TypeToNumber(object: Activity_Type): number { return 0; case Activity_Type.MEMO_COMMENT: return 1; - case Activity_Type.VERSION_UPDATE: - return 2; case Activity_Type.UNRECOGNIZED: default: return -1; diff --git a/web/src/types/proto/api/v1/inbox_service.ts b/web/src/types/proto/api/v1/inbox_service.ts index 6287e7df7..68f3b80b8 100644 --- a/web/src/types/proto/api/v1/inbox_service.ts +++ b/web/src/types/proto/api/v1/inbox_service.ts @@ -89,8 +89,6 @@ export enum Inbox_Type { TYPE_UNSPECIFIED = "TYPE_UNSPECIFIED", /** MEMO_COMMENT - Memo comment notification. */ MEMO_COMMENT = "MEMO_COMMENT", - /** VERSION_UPDATE - Version update notification. */ - VERSION_UPDATE = "VERSION_UPDATE", UNRECOGNIZED = "UNRECOGNIZED", } @@ -102,9 +100,6 @@ export function inbox_TypeFromJSON(object: any): Inbox_Type { case 1: case "MEMO_COMMENT": return Inbox_Type.MEMO_COMMENT; - case 2: - case "VERSION_UPDATE": - return Inbox_Type.VERSION_UPDATE; case -1: case "UNRECOGNIZED": default: @@ -118,8 +113,6 @@ export function inbox_TypeToNumber(object: Inbox_Type): number { return 0; case Inbox_Type.MEMO_COMMENT: return 1; - case Inbox_Type.VERSION_UPDATE: - return 2; case Inbox_Type.UNRECOGNIZED: default: return -1; diff --git a/web/src/types/proto/api/v1/user_service.ts b/web/src/types/proto/api/v1/user_service.ts index fdb68a825..2569af3c4 100644 --- a/web/src/types/proto/api/v1/user_service.ts +++ b/web/src/types/proto/api/v1/user_service.ts @@ -579,6 +579,127 @@ export interface DeleteUserWebhookRequest { name: string; } +export interface UserNotification { + /** + * The resource name of the notification. + * Format: users/{user}/notifications/{notification} + */ + name: string; + /** + * The sender of the notification. + * Format: users/{user} + */ + sender: string; + /** The status of the notification. */ + status: UserNotification_Status; + /** The creation timestamp. */ + createTime?: + | Date + | undefined; + /** The type of the notification. */ + type: UserNotification_Type; + /** The activity ID associated with this notification. */ + activityId?: number | undefined; +} + +export enum UserNotification_Status { + STATUS_UNSPECIFIED = "STATUS_UNSPECIFIED", + UNREAD = "UNREAD", + ARCHIVED = "ARCHIVED", + UNRECOGNIZED = "UNRECOGNIZED", +} + +export function userNotification_StatusFromJSON(object: any): UserNotification_Status { + switch (object) { + case 0: + case "STATUS_UNSPECIFIED": + return UserNotification_Status.STATUS_UNSPECIFIED; + case 1: + case "UNREAD": + return UserNotification_Status.UNREAD; + case 2: + case "ARCHIVED": + return UserNotification_Status.ARCHIVED; + case -1: + case "UNRECOGNIZED": + default: + return UserNotification_Status.UNRECOGNIZED; + } +} + +export function userNotification_StatusToNumber(object: UserNotification_Status): number { + switch (object) { + case UserNotification_Status.STATUS_UNSPECIFIED: + return 0; + case UserNotification_Status.UNREAD: + return 1; + case UserNotification_Status.ARCHIVED: + return 2; + case UserNotification_Status.UNRECOGNIZED: + default: + return -1; + } +} + +export enum UserNotification_Type { + TYPE_UNSPECIFIED = "TYPE_UNSPECIFIED", + MEMO_COMMENT = "MEMO_COMMENT", + UNRECOGNIZED = "UNRECOGNIZED", +} + +export function userNotification_TypeFromJSON(object: any): UserNotification_Type { + switch (object) { + case 0: + case "TYPE_UNSPECIFIED": + return UserNotification_Type.TYPE_UNSPECIFIED; + case 1: + case "MEMO_COMMENT": + return UserNotification_Type.MEMO_COMMENT; + case -1: + case "UNRECOGNIZED": + default: + return UserNotification_Type.UNRECOGNIZED; + } +} + +export function userNotification_TypeToNumber(object: UserNotification_Type): number { + switch (object) { + case UserNotification_Type.TYPE_UNSPECIFIED: + return 0; + case UserNotification_Type.MEMO_COMMENT: + return 1; + case UserNotification_Type.UNRECOGNIZED: + default: + return -1; + } +} + +export interface ListUserNotificationsRequest { + /** + * The parent user resource. + * Format: users/{user} + */ + parent: string; + pageSize: number; + pageToken: string; + filter: string; +} + +export interface ListUserNotificationsResponse { + notifications: UserNotification[]; + nextPageToken: string; +} + +export interface UpdateUserNotificationRequest { + notification?: UserNotification | undefined; + updateMask?: string[] | undefined; +} + +export interface DeleteUserNotificationRequest { + /** Format: users/{user}/notifications/{notification} */ + name: string; +} + function createBaseUser(): User { return { name: "", @@ -3206,190 +3327,549 @@ export const DeleteUserWebhookRequest: MessageFns = { }, }; -export type UserServiceDefinition = typeof UserServiceDefinition; -export const UserServiceDefinition = { - name: "UserService", - fullName: "memos.api.v1.UserService", - methods: { - /** ListUsers returns a list of users. */ - listUsers: { - name: "ListUsers", - requestType: ListUsersRequest, - requestStream: false, - responseType: ListUsersResponse, - responseStream: false, - options: { - _unknownFields: { - 578365826: [new Uint8Array([15, 18, 13, 47, 97, 112, 105, 47, 118, 49, 47, 117, 115, 101, 114, 115])], - }, - }, - }, - /** - * GetUser gets a user by ID or username. - * Supports both numeric IDs and username strings: - * - users/{id} (e.g., users/101) - * - users/{username} (e.g., users/steven) - */ - getUser: { - name: "GetUser", - requestType: GetUserRequest, - requestStream: false, - responseType: User, - responseStream: false, - options: { - _unknownFields: { - 8410: [new Uint8Array([4, 110, 97, 109, 101])], - 578365826: [ - new Uint8Array([ - 24, - 18, - 22, - 47, - 97, - 112, - 105, - 47, - 118, - 49, - 47, - 123, - 110, - 97, - 109, - 101, - 61, - 117, - 115, - 101, - 114, - 115, - 47, - 42, - 125, - ]), - ], - }, - }, - }, - /** CreateUser creates a new user. */ - createUser: { - name: "CreateUser", - requestType: CreateUserRequest, - requestStream: false, - responseType: User, - responseStream: false, - options: { - _unknownFields: { - 8410: [new Uint8Array([4, 117, 115, 101, 114])], - 578365826: [ - new Uint8Array([ - 21, - 58, - 4, - 117, - 115, - 101, - 114, - 34, - 13, - 47, - 97, - 112, - 105, - 47, - 118, - 49, - 47, - 117, - 115, - 101, - 114, - 115, - ]), - ], - }, - }, - }, - /** UpdateUser updates a user. */ - updateUser: { - name: "UpdateUser", - requestType: UpdateUserRequest, - requestStream: false, - responseType: User, - responseStream: false, - options: { - _unknownFields: { - 8410: [new Uint8Array([16, 117, 115, 101, 114, 44, 117, 112, 100, 97, 116, 101, 95, 109, 97, 115, 107])], - 578365826: [ - new Uint8Array([ - 35, - 58, - 4, - 117, - 115, - 101, - 114, - 50, - 27, - 47, - 97, - 112, - 105, - 47, - 118, - 49, - 47, - 123, - 117, - 115, - 101, - 114, - 46, - 110, - 97, - 109, - 101, - 61, - 117, - 115, - 101, - 114, - 115, - 47, - 42, - 125, - ]), - ], - }, - }, - }, - /** DeleteUser deletes a user. */ - deleteUser: { - name: "DeleteUser", - requestType: DeleteUserRequest, - requestStream: false, - responseType: Empty, - responseStream: false, - options: { - _unknownFields: { - 8410: [new Uint8Array([4, 110, 97, 109, 101])], - 578365826: [ - new Uint8Array([ - 24, - 42, - 22, - 47, - 97, - 112, - 105, - 47, - 118, - 49, - 47, - 123, - 110, - 97, - 109, +function createBaseUserNotification(): UserNotification { + return { + name: "", + sender: "", + status: UserNotification_Status.STATUS_UNSPECIFIED, + createTime: undefined, + type: UserNotification_Type.TYPE_UNSPECIFIED, + activityId: undefined, + }; +} + +export const UserNotification: MessageFns = { + encode(message: UserNotification, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.name !== "") { + writer.uint32(10).string(message.name); + } + if (message.sender !== "") { + writer.uint32(18).string(message.sender); + } + if (message.status !== UserNotification_Status.STATUS_UNSPECIFIED) { + writer.uint32(24).int32(userNotification_StatusToNumber(message.status)); + } + if (message.createTime !== undefined) { + Timestamp.encode(toTimestamp(message.createTime), writer.uint32(34).fork()).join(); + } + if (message.type !== UserNotification_Type.TYPE_UNSPECIFIED) { + writer.uint32(40).int32(userNotification_TypeToNumber(message.type)); + } + if (message.activityId !== undefined) { + writer.uint32(48).int32(message.activityId); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): UserNotification { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseUserNotification(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.name = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.sender = reader.string(); + continue; + } + case 3: { + if (tag !== 24) { + break; + } + + message.status = userNotification_StatusFromJSON(reader.int32()); + continue; + } + case 4: { + if (tag !== 34) { + break; + } + + message.createTime = fromTimestamp(Timestamp.decode(reader, reader.uint32())); + continue; + } + case 5: { + if (tag !== 40) { + break; + } + + message.type = userNotification_TypeFromJSON(reader.int32()); + continue; + } + case 6: { + if (tag !== 48) { + break; + } + + message.activityId = reader.int32(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + create(base?: DeepPartial): UserNotification { + return UserNotification.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): UserNotification { + const message = createBaseUserNotification(); + message.name = object.name ?? ""; + message.sender = object.sender ?? ""; + message.status = object.status ?? UserNotification_Status.STATUS_UNSPECIFIED; + message.createTime = object.createTime ?? undefined; + message.type = object.type ?? UserNotification_Type.TYPE_UNSPECIFIED; + message.activityId = object.activityId ?? undefined; + return message; + }, +}; + +function createBaseListUserNotificationsRequest(): ListUserNotificationsRequest { + return { parent: "", pageSize: 0, pageToken: "", filter: "" }; +} + +export const ListUserNotificationsRequest: MessageFns = { + encode(message: ListUserNotificationsRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.parent !== "") { + writer.uint32(10).string(message.parent); + } + if (message.pageSize !== 0) { + writer.uint32(16).int32(message.pageSize); + } + if (message.pageToken !== "") { + writer.uint32(26).string(message.pageToken); + } + if (message.filter !== "") { + writer.uint32(34).string(message.filter); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ListUserNotificationsRequest { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseListUserNotificationsRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.parent = reader.string(); + continue; + } + case 2: { + if (tag !== 16) { + break; + } + + message.pageSize = reader.int32(); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.pageToken = reader.string(); + continue; + } + case 4: { + if (tag !== 34) { + break; + } + + message.filter = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + create(base?: DeepPartial): ListUserNotificationsRequest { + return ListUserNotificationsRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): ListUserNotificationsRequest { + const message = createBaseListUserNotificationsRequest(); + message.parent = object.parent ?? ""; + message.pageSize = object.pageSize ?? 0; + message.pageToken = object.pageToken ?? ""; + message.filter = object.filter ?? ""; + return message; + }, +}; + +function createBaseListUserNotificationsResponse(): ListUserNotificationsResponse { + return { notifications: [], nextPageToken: "" }; +} + +export const ListUserNotificationsResponse: MessageFns = { + encode(message: ListUserNotificationsResponse, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + for (const v of message.notifications) { + UserNotification.encode(v!, writer.uint32(10).fork()).join(); + } + if (message.nextPageToken !== "") { + writer.uint32(18).string(message.nextPageToken); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ListUserNotificationsResponse { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseListUserNotificationsResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.notifications.push(UserNotification.decode(reader, reader.uint32())); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.nextPageToken = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + create(base?: DeepPartial): ListUserNotificationsResponse { + return ListUserNotificationsResponse.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): ListUserNotificationsResponse { + const message = createBaseListUserNotificationsResponse(); + message.notifications = object.notifications?.map((e) => UserNotification.fromPartial(e)) || []; + message.nextPageToken = object.nextPageToken ?? ""; + return message; + }, +}; + +function createBaseUpdateUserNotificationRequest(): UpdateUserNotificationRequest { + return { notification: undefined, updateMask: undefined }; +} + +export const UpdateUserNotificationRequest: MessageFns = { + encode(message: UpdateUserNotificationRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.notification !== undefined) { + UserNotification.encode(message.notification, writer.uint32(10).fork()).join(); + } + if (message.updateMask !== undefined) { + FieldMask.encode(FieldMask.wrap(message.updateMask), writer.uint32(18).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): UpdateUserNotificationRequest { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseUpdateUserNotificationRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.notification = UserNotification.decode(reader, reader.uint32()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.updateMask = FieldMask.unwrap(FieldMask.decode(reader, reader.uint32())); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + create(base?: DeepPartial): UpdateUserNotificationRequest { + return UpdateUserNotificationRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): UpdateUserNotificationRequest { + const message = createBaseUpdateUserNotificationRequest(); + message.notification = (object.notification !== undefined && object.notification !== null) + ? UserNotification.fromPartial(object.notification) + : undefined; + message.updateMask = object.updateMask ?? undefined; + return message; + }, +}; + +function createBaseDeleteUserNotificationRequest(): DeleteUserNotificationRequest { + return { name: "" }; +} + +export const DeleteUserNotificationRequest: MessageFns = { + encode(message: DeleteUserNotificationRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.name !== "") { + writer.uint32(10).string(message.name); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): DeleteUserNotificationRequest { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseDeleteUserNotificationRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.name = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + create(base?: DeepPartial): DeleteUserNotificationRequest { + return DeleteUserNotificationRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): DeleteUserNotificationRequest { + const message = createBaseDeleteUserNotificationRequest(); + message.name = object.name ?? ""; + return message; + }, +}; + +export type UserServiceDefinition = typeof UserServiceDefinition; +export const UserServiceDefinition = { + name: "UserService", + fullName: "memos.api.v1.UserService", + methods: { + /** ListUsers returns a list of users. */ + listUsers: { + name: "ListUsers", + requestType: ListUsersRequest, + requestStream: false, + responseType: ListUsersResponse, + responseStream: false, + options: { + _unknownFields: { + 578365826: [new Uint8Array([15, 18, 13, 47, 97, 112, 105, 47, 118, 49, 47, 117, 115, 101, 114, 115])], + }, + }, + }, + /** + * GetUser gets a user by ID or username. + * Supports both numeric IDs and username strings: + * - users/{id} (e.g., users/101) + * - users/{username} (e.g., users/steven) + */ + getUser: { + name: "GetUser", + requestType: GetUserRequest, + requestStream: false, + responseType: User, + responseStream: false, + options: { + _unknownFields: { + 8410: [new Uint8Array([4, 110, 97, 109, 101])], + 578365826: [ + new Uint8Array([ + 24, + 18, + 22, + 47, + 97, + 112, + 105, + 47, + 118, + 49, + 47, + 123, + 110, + 97, + 109, + 101, + 61, + 117, + 115, + 101, + 114, + 115, + 47, + 42, + 125, + ]), + ], + }, + }, + }, + /** CreateUser creates a new user. */ + createUser: { + name: "CreateUser", + requestType: CreateUserRequest, + requestStream: false, + responseType: User, + responseStream: false, + options: { + _unknownFields: { + 8410: [new Uint8Array([4, 117, 115, 101, 114])], + 578365826: [ + new Uint8Array([ + 21, + 58, + 4, + 117, + 115, + 101, + 114, + 34, + 13, + 47, + 97, + 112, + 105, + 47, + 118, + 49, + 47, + 117, + 115, + 101, + 114, + 115, + ]), + ], + }, + }, + }, + /** UpdateUser updates a user. */ + updateUser: { + name: "UpdateUser", + requestType: UpdateUserRequest, + requestStream: false, + responseType: User, + responseStream: false, + options: { + _unknownFields: { + 8410: [new Uint8Array([16, 117, 115, 101, 114, 44, 117, 112, 100, 97, 116, 101, 95, 109, 97, 115, 107])], + 578365826: [ + new Uint8Array([ + 35, + 58, + 4, + 117, + 115, + 101, + 114, + 50, + 27, + 47, + 97, + 112, + 105, + 47, + 118, + 49, + 47, + 123, + 117, + 115, + 101, + 114, + 46, + 110, + 97, + 109, + 101, + 61, + 117, + 115, + 101, + 114, + 115, + 47, + 42, + 125, + ]), + ], + }, + }, + }, + /** DeleteUser deletes a user. */ + deleteUser: { + name: "DeleteUser", + requestType: DeleteUserRequest, + requestStream: false, + responseType: Empty, + responseStream: false, + options: { + _unknownFields: { + 8410: [new Uint8Array([4, 110, 97, 109, 101])], + 578365826: [ + new Uint8Array([ + 24, + 42, + 22, + 47, + 97, + 112, + 105, + 47, + 118, + 49, + 47, + 123, + 110, + 97, + 109, 101, 61, 117, @@ -4317,6 +4797,235 @@ export const UserServiceDefinition = { }, }, }, + /** ListUserNotifications lists notifications for a user. */ + listUserNotifications: { + name: "ListUserNotifications", + requestType: ListUserNotificationsRequest, + requestStream: false, + responseType: ListUserNotificationsResponse, + responseStream: false, + options: { + _unknownFields: { + 8410: [new Uint8Array([6, 112, 97, 114, 101, 110, 116])], + 578365826: [ + new Uint8Array([ + 40, + 18, + 38, + 47, + 97, + 112, + 105, + 47, + 118, + 49, + 47, + 123, + 112, + 97, + 114, + 101, + 110, + 116, + 61, + 117, + 115, + 101, + 114, + 115, + 47, + 42, + 125, + 47, + 110, + 111, + 116, + 105, + 102, + 105, + 99, + 97, + 116, + 105, + 111, + 110, + 115, + ]), + ], + }, + }, + }, + /** UpdateUserNotification updates a notification. */ + updateUserNotification: { + name: "UpdateUserNotification", + requestType: UpdateUserNotificationRequest, + requestStream: false, + responseType: UserNotification, + responseStream: false, + options: { + _unknownFields: { + 8410: [ + new Uint8Array([ + 24, + 110, + 111, + 116, + 105, + 102, + 105, + 99, + 97, + 116, + 105, + 111, + 110, + 44, + 117, + 112, + 100, + 97, + 116, + 101, + 95, + 109, + 97, + 115, + 107, + ]), + ], + 578365826: [ + new Uint8Array([ + 67, + 58, + 12, + 110, + 111, + 116, + 105, + 102, + 105, + 99, + 97, + 116, + 105, + 111, + 110, + 50, + 51, + 47, + 97, + 112, + 105, + 47, + 118, + 49, + 47, + 123, + 110, + 111, + 116, + 105, + 102, + 105, + 99, + 97, + 116, + 105, + 111, + 110, + 46, + 110, + 97, + 109, + 101, + 61, + 117, + 115, + 101, + 114, + 115, + 47, + 42, + 47, + 110, + 111, + 116, + 105, + 102, + 105, + 99, + 97, + 116, + 105, + 111, + 110, + 115, + 47, + 42, + 125, + ]), + ], + }, + }, + }, + /** DeleteUserNotification deletes a notification. */ + deleteUserNotification: { + name: "DeleteUserNotification", + requestType: DeleteUserNotificationRequest, + requestStream: false, + responseType: Empty, + responseStream: false, + options: { + _unknownFields: { + 8410: [new Uint8Array([4, 110, 97, 109, 101])], + 578365826: [ + new Uint8Array([ + 40, + 42, + 38, + 47, + 97, + 112, + 105, + 47, + 118, + 49, + 47, + 123, + 110, + 97, + 109, + 101, + 61, + 117, + 115, + 101, + 114, + 115, + 47, + 42, + 47, + 110, + 111, + 116, + 105, + 102, + 105, + 99, + 97, + 116, + 105, + 111, + 110, + 115, + 47, + 42, + 125, + ]), + ], + }, + }, + }, }, } as const;