diff --git a/gqlgen.yml b/gqlgen.yml index c2e9a16..ecdd018 100644 --- a/gqlgen.yml +++ b/gqlgen.yml @@ -49,6 +49,8 @@ models: model: tailly_back_v2/internal/domain.ClipLike ClipComment: model: tailly_back_v2/internal/domain.ClipComment + LikeNotification: + model: tailly_back_v2/internal/domain.LikeNotification autobind: - "tailly_back_v2/internal/domain" \ No newline at end of file diff --git a/internal/domain/models.go b/internal/domain/models.go index bd7b8e7..6aa9430 100644 --- a/internal/domain/models.go +++ b/internal/domain/models.go @@ -23,6 +23,7 @@ type Post struct { Title string `json:"title"` Content string `json:"content"` AuthorID int `json:"authorId"` + Author *User `json:"author,omitempty"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } @@ -39,10 +40,38 @@ type Comment struct { // Лайк к посту type Like struct { - ID int `json:"id"` - PostID int `json:"postId"` - UserID int `json:"userId"` - CreatedAt time.Time `json:"createdAt"` + ID int `json:"id"` + PostID int `json:"postId"` + UserID int `json:"userId"` + CreatedAt time.Time `json:"createdAt"` + IsRead bool `json:"isRead"` + NotifiedAt time.Time `json:"notifiedAt"` +} + +type LikeNotification struct { + ID int `json:"id"` + LikerID int `json:"-"` // Для внутреннего использования + Liker *User `json:"liker"` // Для GraphQL + PostID int `json:"-"` // Для внутреннего использования + Post *Post `json:"post"` // Для GraphQL + IsRead bool `json:"isRead"` + CreatedAt time.Time `json:"-"` + CreatedAtStr string `json:"createdAt"` // Для GraphQL +} + +// Вспомогательные методы +func (n *LikeNotification) SetLiker(user *User) { + n.Liker = user + n.LikerID = user.ID +} + +func (n *LikeNotification) SetPost(post *Post) { + n.Post = post + n.PostID = post.ID +} + +func (n *LikeNotification) SetCreatedAtStr() { + n.CreatedAtStr = n.CreatedAt.Format(time.RFC3339) } // Токены для аутентификации diff --git a/internal/http/graph/generated.go b/internal/http/graph/generated.go index 4a7654c..a42fc7f 100644 --- a/internal/http/graph/generated.go +++ b/internal/http/graph/generated.go @@ -47,6 +47,7 @@ type ResolverRoot interface { Comment() CommentResolver Device() DeviceResolver Like() LikeResolver + LikeNotification() LikeNotificationResolver Message() MessageResolver Mutation() MutationResolver Post() PostResolver @@ -146,10 +147,31 @@ type ComplexityRoot struct { } Like struct { + CreatedAt func(childComplexity int) int + ID func(childComplexity int) int + IsRead func(childComplexity int) int + NotifiedAt func(childComplexity int) int + Post func(childComplexity int) int + User func(childComplexity int) int + } + + LikeNotification struct { CreatedAt func(childComplexity int) int ID func(childComplexity int) int + IsRead func(childComplexity int) int + Liker func(childComplexity int) int Post func(childComplexity int) int - User func(childComplexity int) int + } + + LikeNotificationsResponse struct { + Notifications func(childComplexity int) int + TotalCount func(childComplexity int) int + UnreadCount func(childComplexity int) int + } + + MarkLikeNotificationReadResult struct { + Message func(childComplexity int) int + Success func(childComplexity int) int } MarkNotificationReadResult struct { @@ -168,33 +190,35 @@ type ComplexityRoot struct { } Mutation struct { - ChangePassword func(childComplexity int, oldPassword string, newPassword string) int - ConfirmEmail func(childComplexity int, token string) int - CreateChat func(childComplexity int, user1Id int, user2Id int) int - CreateClip func(childComplexity int, title *string, video graphql.Upload) int - CreateClipComment func(childComplexity int, clipID int, content string) int - CreateComment func(childComplexity int, postID int, content string) int - CreatePost func(childComplexity int, title string, content graphql.Upload) int - DeleteClip func(childComplexity int, id int) int - DeleteClipComment func(childComplexity int, commentID int) int - DeletePost func(childComplexity int, id int) int - FollowUser func(childComplexity int, followingID int) int - LikeClip func(childComplexity int, clipID int) int - LikePost func(childComplexity int, postID int) int - Login func(childComplexity int, input domain.LoginInput) int - MarkNotificationAsRead func(childComplexity int, notificationID int) int - RefreshTokens func(childComplexity int, refreshToken string) int - Register func(childComplexity int, input domain.RegisterInput) int - RenameDevice func(childComplexity int, deviceID int, name string) int - RequestEmailConfirmation func(childComplexity int) int - ResendEmailConfirmation func(childComplexity int) int - SendMessage func(childComplexity int, chatID int, content string) int - TerminateSession func(childComplexity int, sessionID int) int - UnfollowUser func(childComplexity int, followingID int) int - UnlikeClip func(childComplexity int, clipID int) int - UnlikePost func(childComplexity int, postID int) int - UpdateMessageStatus func(childComplexity int, messageID int, status MessageStatus) int - UpdateProfile func(childComplexity int, username string, email string, avatar string) int + ChangePassword func(childComplexity int, oldPassword string, newPassword string) int + ConfirmEmail func(childComplexity int, token string) int + CreateChat func(childComplexity int, user1Id int, user2Id int) int + CreateClip func(childComplexity int, title *string, video graphql.Upload) int + CreateClipComment func(childComplexity int, clipID int, content string) int + CreateComment func(childComplexity int, postID int, content string) int + CreatePost func(childComplexity int, title string, content graphql.Upload) int + DeleteClip func(childComplexity int, id int) int + DeleteClipComment func(childComplexity int, commentID int) int + DeletePost func(childComplexity int, id int) int + FollowUser func(childComplexity int, followingID int) int + LikeClip func(childComplexity int, clipID int) int + LikePost func(childComplexity int, postID int) int + Login func(childComplexity int, input domain.LoginInput) int + MarkAllLikeNotificationsAsRead func(childComplexity int) int + MarkLikeNotificationAsRead func(childComplexity int, notificationID int) int + MarkNotificationAsRead func(childComplexity int, notificationID int) int + RefreshTokens func(childComplexity int, refreshToken string) int + Register func(childComplexity int, input domain.RegisterInput) int + RenameDevice func(childComplexity int, deviceID int, name string) int + RequestEmailConfirmation func(childComplexity int) int + ResendEmailConfirmation func(childComplexity int) int + SendMessage func(childComplexity int, chatID int, content string) int + TerminateSession func(childComplexity int, sessionID int) int + UnfollowUser func(childComplexity int, followingID int) int + UnlikeClip func(childComplexity int, clipID int) int + UnlikePost func(childComplexity int, postID int) int + UpdateMessageStatus func(childComplexity int, messageID int, status MessageStatus) int + UpdateProfile func(childComplexity int, username string, email string, avatar string) int } NotificationsResponse struct { @@ -228,6 +252,7 @@ type ComplexityRoot struct { GetFollowersCount func(childComplexity int, userID int) int GetFollowing func(childComplexity int, userID int, limit *int, offset *int) int GetFollowingCount func(childComplexity int, userID int) int + GetLikeNotifications func(childComplexity int, unreadOnly *bool, limit *int, offset *int) int GetSubscriptionNotifications func(childComplexity int, unreadOnly *bool, limit *int, offset *int) int GetUserChats func(childComplexity int, userID int) int GetUserPosts func(childComplexity int, userID int) int @@ -292,6 +317,7 @@ type ComplexityRoot struct { FollowingCount func(childComplexity int) int ID func(childComplexity int) int IsFollowing func(childComplexity int, userID int) int + LikeNotifications func(childComplexity int, unreadOnly *bool, limit *int, offset *int) int SubscriptionNotifications func(childComplexity int, unreadOnly *bool, limit *int, offset *int) int UpdatedAt func(childComplexity int) int Username func(childComplexity int) int @@ -331,6 +357,11 @@ type LikeResolver interface { Post(ctx context.Context, obj *domain.Like) (*domain.Post, error) User(ctx context.Context, obj *domain.Like) (*domain.User, error) CreatedAt(ctx context.Context, obj *domain.Like) (string, error) + + NotifiedAt(ctx context.Context, obj *domain.Like) (*string, error) +} +type LikeNotificationResolver interface { + CreatedAt(ctx context.Context, obj *domain.LikeNotification) (string, error) } type MessageResolver interface { Status(ctx context.Context, obj *domain.Message) (MessageStatus, error) @@ -364,6 +395,8 @@ type MutationResolver interface { UnlikeClip(ctx context.Context, clipID int) (bool, error) CreateClipComment(ctx context.Context, clipID int, content string) (*domain.ClipComment, error) DeleteClipComment(ctx context.Context, commentID int) (bool, error) + MarkLikeNotificationAsRead(ctx context.Context, notificationID int) (*MarkLikeNotificationReadResult, error) + MarkAllLikeNotificationsAsRead(ctx context.Context) (*MarkLikeNotificationReadResult, error) } type PostResolver interface { Author(ctx context.Context, obj *domain.Post) (*domain.User, error) @@ -399,6 +432,7 @@ type QueryResolver interface { ClipComments(ctx context.Context, clipID int, limit *int, offset *int) ([]*domain.ClipComment, error) ClipLikes(ctx context.Context, clipID int, limit *int, offset *int) ([]*domain.ClipLike, error) IsLiked(ctx context.Context, clipID int) (bool, error) + GetLikeNotifications(ctx context.Context, unreadOnly *bool, limit *int, offset *int) (*LikeNotificationsResponse, error) } type SessionResolver interface { StartedAt(ctx context.Context, obj *domain.Session) (string, error) @@ -424,6 +458,7 @@ type UserResolver interface { Followers(ctx context.Context, obj *domain.User, limit *int, offset *int) (*FollowersResponse, error) Following(ctx context.Context, obj *domain.User, limit *int, offset *int) (*FollowingResponse, error) SubscriptionNotifications(ctx context.Context, obj *domain.User, unreadOnly *bool, limit *int, offset *int) (*NotificationsResponse, error) + LikeNotifications(ctx context.Context, obj *domain.User, unreadOnly *bool, limit *int, offset *int) (*LikeNotificationsResponse, error) } type executableSchema struct { @@ -816,6 +851,20 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.Like.ID(childComplexity), true + case "Like.isRead": + if e.complexity.Like.IsRead == nil { + break + } + + return e.complexity.Like.IsRead(childComplexity), true + + case "Like.notifiedAt": + if e.complexity.Like.NotifiedAt == nil { + break + } + + return e.complexity.Like.NotifiedAt(childComplexity), true + case "Like.post": if e.complexity.Like.Post == nil { break @@ -830,6 +879,76 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.Like.User(childComplexity), true + case "LikeNotification.createdAt": + if e.complexity.LikeNotification.CreatedAt == nil { + break + } + + return e.complexity.LikeNotification.CreatedAt(childComplexity), true + + case "LikeNotification.id": + if e.complexity.LikeNotification.ID == nil { + break + } + + return e.complexity.LikeNotification.ID(childComplexity), true + + case "LikeNotification.isRead": + if e.complexity.LikeNotification.IsRead == nil { + break + } + + return e.complexity.LikeNotification.IsRead(childComplexity), true + + case "LikeNotification.liker": + if e.complexity.LikeNotification.Liker == nil { + break + } + + return e.complexity.LikeNotification.Liker(childComplexity), true + + case "LikeNotification.post": + if e.complexity.LikeNotification.Post == nil { + break + } + + return e.complexity.LikeNotification.Post(childComplexity), true + + case "LikeNotificationsResponse.notifications": + if e.complexity.LikeNotificationsResponse.Notifications == nil { + break + } + + return e.complexity.LikeNotificationsResponse.Notifications(childComplexity), true + + case "LikeNotificationsResponse.totalCount": + if e.complexity.LikeNotificationsResponse.TotalCount == nil { + break + } + + return e.complexity.LikeNotificationsResponse.TotalCount(childComplexity), true + + case "LikeNotificationsResponse.unreadCount": + if e.complexity.LikeNotificationsResponse.UnreadCount == nil { + break + } + + return e.complexity.LikeNotificationsResponse.UnreadCount(childComplexity), true + + case "MarkLikeNotificationReadResult.message": + if e.complexity.MarkLikeNotificationReadResult.Message == nil { + break + } + + return e.complexity.MarkLikeNotificationReadResult.Message(childComplexity), true + + case "MarkLikeNotificationReadResult.success": + if e.complexity.MarkLikeNotificationReadResult.Success == nil { + break + } + + return e.complexity.MarkLikeNotificationReadResult.Success(childComplexity), true + case "MarkNotificationReadResult.message": if e.complexity.MarkNotificationReadResult.Message == nil { break @@ -1061,6 +1180,25 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.Mutation.Login(childComplexity, args["input"].(domain.LoginInput)), true + case "Mutation.markAllLikeNotificationsAsRead": + if e.complexity.Mutation.MarkAllLikeNotificationsAsRead == nil { + break + } + + return e.complexity.Mutation.MarkAllLikeNotificationsAsRead(childComplexity), true + + case "Mutation.markLikeNotificationAsRead": + if e.complexity.Mutation.MarkLikeNotificationAsRead == nil { + break + } + + args, err := ec.field_Mutation_markLikeNotificationAsRead_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.MarkLikeNotificationAsRead(childComplexity, args["notificationId"].(int)), true + case "Mutation.markNotificationAsRead": if e.complexity.Mutation.MarkNotificationAsRead == nil { break @@ -1430,6 +1568,18 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.Query.GetFollowingCount(childComplexity, args["userId"].(int)), true + case "Query.getLikeNotifications": + if e.complexity.Query.GetLikeNotifications == nil { + break + } + + args, err := ec.field_Query_getLikeNotifications_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.GetLikeNotifications(childComplexity, args["unreadOnly"].(*bool), args["limit"].(*int), args["offset"].(*int)), true + case "Query.getSubscriptionNotifications": if e.complexity.Query.GetSubscriptionNotifications == nil { break @@ -1827,6 +1977,18 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.User.IsFollowing(childComplexity, args["userId"].(int)), true + case "User.likeNotifications": + if e.complexity.User.LikeNotifications == nil { + break + } + + args, err := ec.field_User_likeNotifications_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.User.LikeNotifications(childComplexity, args["unreadOnly"].(*bool), args["limit"].(*int), args["offset"].(*int)), true + case "User.subscriptionNotifications": if e.complexity.User.SubscriptionNotifications == nil { break @@ -2200,6 +2362,17 @@ func (ec *executionContext) field_Mutation_login_args(ctx context.Context, rawAr return args, nil } +func (ec *executionContext) field_Mutation_markLikeNotificationAsRead_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := processArgField(ctx, rawArgs, "notificationId", ec.unmarshalNInt2int) + if err != nil { + return nil, err + } + args["notificationId"] = arg0 + return args, nil +} + func (ec *executionContext) field_Mutation_markNotificationAsRead_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -2538,6 +2711,27 @@ func (ec *executionContext) field_Query_getFollowing_args(ctx context.Context, r return args, nil } +func (ec *executionContext) field_Query_getLikeNotifications_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := processArgField(ctx, rawArgs, "unreadOnly", ec.unmarshalOBoolean2ᚖbool) + if err != nil { + return nil, err + } + args["unreadOnly"] = arg0 + arg1, err := processArgField(ctx, rawArgs, "limit", ec.unmarshalOInt2ᚖint) + if err != nil { + return nil, err + } + args["limit"] = arg1 + arg2, err := processArgField(ctx, rawArgs, "offset", ec.unmarshalOInt2ᚖint) + if err != nil { + return nil, err + } + args["offset"] = arg2 + return args, nil +} + func (ec *executionContext) field_Query_getSubscriptionNotifications_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -2738,6 +2932,27 @@ func (ec *executionContext) field_User_isFollowing_args(ctx context.Context, raw return args, nil } +func (ec *executionContext) field_User_likeNotifications_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := processArgField(ctx, rawArgs, "unreadOnly", ec.unmarshalOBoolean2ᚖbool) + if err != nil { + return nil, err + } + args["unreadOnly"] = arg0 + arg1, err := processArgField(ctx, rawArgs, "limit", ec.unmarshalOInt2ᚖint) + if err != nil { + return nil, err + } + args["limit"] = arg1 + arg2, err := processArgField(ctx, rawArgs, "offset", ec.unmarshalOInt2ᚖint) + if err != nil { + return nil, err + } + args["offset"] = arg2 + return args, nil +} + func (ec *executionContext) field_User_subscriptionNotifications_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -3329,6 +3544,8 @@ func (ec *executionContext) fieldContext_Clip_author(_ context.Context, field gr return ec.fieldContext_User_following(ctx, field) case "subscriptionNotifications": return ec.fieldContext_User_subscriptionNotifications(ctx, field) + case "likeNotifications": + return ec.fieldContext_User_likeNotifications(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -3709,6 +3926,8 @@ func (ec *executionContext) fieldContext_ClipComment_author(_ context.Context, f return ec.fieldContext_User_following(ctx, field) case "subscriptionNotifications": return ec.fieldContext_User_subscriptionNotifications(ctx, field) + case "likeNotifications": + return ec.fieldContext_User_likeNotifications(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -4243,6 +4462,8 @@ func (ec *executionContext) fieldContext_Comment_author(_ context.Context, field return ec.fieldContext_User_following(ctx, field) case "subscriptionNotifications": return ec.fieldContext_User_subscriptionNotifications(ctx, field) + case "likeNotifications": + return ec.fieldContext_User_likeNotifications(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -5369,6 +5590,8 @@ func (ec *executionContext) fieldContext_Like_user(_ context.Context, field grap return ec.fieldContext_User_following(ctx, field) case "subscriptionNotifications": return ec.fieldContext_User_subscriptionNotifications(ctx, field) + case "likeNotifications": + return ec.fieldContext_User_likeNotifications(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -5420,6 +5643,595 @@ func (ec *executionContext) fieldContext_Like_createdAt(_ context.Context, field return fc, nil } +func (ec *executionContext) _Like_isRead(ctx context.Context, field graphql.CollectedField, obj *domain.Like) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Like_isRead(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsRead, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Like_isRead(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Like", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Like_notifiedAt(ctx context.Context, field graphql.CollectedField, obj *domain.Like) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Like_notifiedAt(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Like().NotifiedAt(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Like_notifiedAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Like", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _LikeNotification_id(ctx context.Context, field graphql.CollectedField, obj *domain.LikeNotification) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_LikeNotification_id(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_LikeNotification_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "LikeNotification", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _LikeNotification_liker(ctx context.Context, field graphql.CollectedField, obj *domain.LikeNotification) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_LikeNotification_liker(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Liker, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*domain.User) + fc.Result = res + return ec.marshalNUser2ᚖtailly_back_v2ᚋinternalᚋdomainᚐUser(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_LikeNotification_liker(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "LikeNotification", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_User_id(ctx, field) + case "username": + return ec.fieldContext_User_username(ctx, field) + case "avatar": + return ec.fieldContext_User_avatar(ctx, field) + case "email": + return ec.fieldContext_User_email(ctx, field) + case "emailConfirmedAt": + return ec.fieldContext_User_emailConfirmedAt(ctx, field) + case "createdAt": + return ec.fieldContext_User_createdAt(ctx, field) + case "updatedAt": + return ec.fieldContext_User_updatedAt(ctx, field) + case "followersCount": + return ec.fieldContext_User_followersCount(ctx, field) + case "followingCount": + return ec.fieldContext_User_followingCount(ctx, field) + case "isFollowing": + return ec.fieldContext_User_isFollowing(ctx, field) + case "followers": + return ec.fieldContext_User_followers(ctx, field) + case "following": + return ec.fieldContext_User_following(ctx, field) + case "subscriptionNotifications": + return ec.fieldContext_User_subscriptionNotifications(ctx, field) + case "likeNotifications": + return ec.fieldContext_User_likeNotifications(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type User", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _LikeNotification_post(ctx context.Context, field graphql.CollectedField, obj *domain.LikeNotification) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_LikeNotification_post(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Post, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*domain.Post) + fc.Result = res + return ec.marshalNPost2ᚖtailly_back_v2ᚋinternalᚋdomainᚐPost(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_LikeNotification_post(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "LikeNotification", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Post_id(ctx, field) + case "title": + return ec.fieldContext_Post_title(ctx, field) + case "content": + return ec.fieldContext_Post_content(ctx, field) + case "author": + return ec.fieldContext_Post_author(ctx, field) + case "commentsCount": + return ec.fieldContext_Post_commentsCount(ctx, field) + case "likes": + return ec.fieldContext_Post_likes(ctx, field) + case "likesCount": + return ec.fieldContext_Post_likesCount(ctx, field) + case "isLiked": + return ec.fieldContext_Post_isLiked(ctx, field) + case "createdAt": + return ec.fieldContext_Post_createdAt(ctx, field) + case "updatedAt": + return ec.fieldContext_Post_updatedAt(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Post", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _LikeNotification_isRead(ctx context.Context, field graphql.CollectedField, obj *domain.LikeNotification) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_LikeNotification_isRead(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsRead, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_LikeNotification_isRead(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "LikeNotification", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _LikeNotification_createdAt(ctx context.Context, field graphql.CollectedField, obj *domain.LikeNotification) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_LikeNotification_createdAt(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.LikeNotification().CreatedAt(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_LikeNotification_createdAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "LikeNotification", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _LikeNotificationsResponse_notifications(ctx context.Context, field graphql.CollectedField, obj *LikeNotificationsResponse) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_LikeNotificationsResponse_notifications(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Notifications, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*domain.LikeNotification) + fc.Result = res + return ec.marshalNLikeNotification2ᚕᚖtailly_back_v2ᚋinternalᚋdomainᚐLikeNotificationᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_LikeNotificationsResponse_notifications(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "LikeNotificationsResponse", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_LikeNotification_id(ctx, field) + case "liker": + return ec.fieldContext_LikeNotification_liker(ctx, field) + case "post": + return ec.fieldContext_LikeNotification_post(ctx, field) + case "isRead": + return ec.fieldContext_LikeNotification_isRead(ctx, field) + case "createdAt": + return ec.fieldContext_LikeNotification_createdAt(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type LikeNotification", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _LikeNotificationsResponse_totalCount(ctx context.Context, field graphql.CollectedField, obj *LikeNotificationsResponse) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_LikeNotificationsResponse_totalCount(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.TotalCount, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_LikeNotificationsResponse_totalCount(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "LikeNotificationsResponse", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _LikeNotificationsResponse_unreadCount(ctx context.Context, field graphql.CollectedField, obj *LikeNotificationsResponse) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_LikeNotificationsResponse_unreadCount(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.UnreadCount, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_LikeNotificationsResponse_unreadCount(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "LikeNotificationsResponse", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _MarkLikeNotificationReadResult_success(ctx context.Context, field graphql.CollectedField, obj *MarkLikeNotificationReadResult) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_MarkLikeNotificationReadResult_success(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Success, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_MarkLikeNotificationReadResult_success(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "MarkLikeNotificationReadResult", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _MarkLikeNotificationReadResult_message(ctx context.Context, field graphql.CollectedField, obj *MarkLikeNotificationReadResult) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_MarkLikeNotificationReadResult_message(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Message, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_MarkLikeNotificationReadResult_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "MarkLikeNotificationReadResult", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _MarkNotificationReadResult_success(ctx context.Context, field graphql.CollectedField, obj *MarkNotificationReadResult) (ret graphql.Marshaler) { fc, err := ec.fieldContext_MarkNotificationReadResult_success(ctx, field) if err != nil { @@ -5881,6 +6693,8 @@ func (ec *executionContext) fieldContext_Mutation_register(ctx context.Context, return ec.fieldContext_User_following(ctx, field) case "subscriptionNotifications": return ec.fieldContext_User_subscriptionNotifications(ctx, field) + case "likeNotifications": + return ec.fieldContext_User_likeNotifications(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -6226,6 +7040,10 @@ func (ec *executionContext) fieldContext_Mutation_likePost(ctx context.Context, return ec.fieldContext_Like_user(ctx, field) case "createdAt": return ec.fieldContext_Like_createdAt(ctx, field) + case "isRead": + return ec.fieldContext_Like_isRead(ctx, field) + case "notifiedAt": + return ec.fieldContext_Like_notifiedAt(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type Like", field.Name) }, @@ -6364,6 +7182,8 @@ func (ec *executionContext) fieldContext_Mutation_updateProfile(ctx context.Cont return ec.fieldContext_User_following(ctx, field) case "subscriptionNotifications": return ec.fieldContext_User_subscriptionNotifications(ctx, field) + case "likeNotifications": + return ec.fieldContext_User_likeNotifications(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -7527,6 +8347,117 @@ func (ec *executionContext) fieldContext_Mutation_deleteClipComment(ctx context. return fc, nil } +func (ec *executionContext) _Mutation_markLikeNotificationAsRead(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_markLikeNotificationAsRead(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().MarkLikeNotificationAsRead(rctx, fc.Args["notificationId"].(int)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*MarkLikeNotificationReadResult) + fc.Result = res + return ec.marshalNMarkLikeNotificationReadResult2ᚖtailly_back_v2ᚋinternalᚋhttpᚋgraphᚐMarkLikeNotificationReadResult(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_markLikeNotificationAsRead(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "success": + return ec.fieldContext_MarkLikeNotificationReadResult_success(ctx, field) + case "message": + return ec.fieldContext_MarkLikeNotificationReadResult_message(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type MarkLikeNotificationReadResult", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_markLikeNotificationAsRead_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mutation_markAllLikeNotificationsAsRead(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_markAllLikeNotificationsAsRead(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().MarkAllLikeNotificationsAsRead(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*MarkLikeNotificationReadResult) + fc.Result = res + return ec.marshalNMarkLikeNotificationReadResult2ᚖtailly_back_v2ᚋinternalᚋhttpᚋgraphᚐMarkLikeNotificationReadResult(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_markAllLikeNotificationsAsRead(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "success": + return ec.fieldContext_MarkLikeNotificationReadResult_success(ctx, field) + case "message": + return ec.fieldContext_MarkLikeNotificationReadResult_message(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type MarkLikeNotificationReadResult", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _NotificationsResponse_notifications(ctx context.Context, field graphql.CollectedField, obj *NotificationsResponse) (ret graphql.Marshaler) { fc, err := ec.fieldContext_NotificationsResponse_notifications(ctx, field) if err != nil { @@ -7872,6 +8803,8 @@ func (ec *executionContext) fieldContext_Post_author(_ context.Context, field gr return ec.fieldContext_User_following(ctx, field) case "subscriptionNotifications": return ec.fieldContext_User_subscriptionNotifications(ctx, field) + case "likeNotifications": + return ec.fieldContext_User_likeNotifications(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -7970,6 +8903,10 @@ func (ec *executionContext) fieldContext_Post_likes(_ context.Context, field gra return ec.fieldContext_Like_user(ctx, field) case "createdAt": return ec.fieldContext_Like_createdAt(ctx, field) + case "isRead": + return ec.fieldContext_Like_isRead(ctx, field) + case "notifiedAt": + return ec.fieldContext_Like_notifiedAt(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type Like", field.Name) }, @@ -8218,6 +9155,8 @@ func (ec *executionContext) fieldContext_Query_me(_ context.Context, field graph return ec.fieldContext_User_following(ctx, field) case "subscriptionNotifications": return ec.fieldContext_User_subscriptionNotifications(ctx, field) + case "likeNotifications": + return ec.fieldContext_User_likeNotifications(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -8587,6 +9526,8 @@ func (ec *executionContext) fieldContext_Query_user(ctx context.Context, field g return ec.fieldContext_User_following(ctx, field) case "subscriptionNotifications": return ec.fieldContext_User_subscriptionNotifications(ctx, field) + case "likeNotifications": + return ec.fieldContext_User_likeNotifications(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -8670,6 +9611,8 @@ func (ec *executionContext) fieldContext_Query_users(_ context.Context, field gr return ec.fieldContext_User_following(ctx, field) case "subscriptionNotifications": return ec.fieldContext_User_subscriptionNotifications(ctx, field) + case "likeNotifications": + return ec.fieldContext_User_likeNotifications(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -9781,6 +10724,69 @@ func (ec *executionContext) fieldContext_Query_isLiked(ctx context.Context, fiel return fc, nil } +func (ec *executionContext) _Query_getLikeNotifications(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_getLikeNotifications(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().GetLikeNotifications(rctx, fc.Args["unreadOnly"].(*bool), fc.Args["limit"].(*int), fc.Args["offset"].(*int)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*LikeNotificationsResponse) + fc.Result = res + return ec.marshalNLikeNotificationsResponse2ᚖtailly_back_v2ᚋinternalᚋhttpᚋgraphᚐLikeNotificationsResponse(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_getLikeNotifications(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "notifications": + return ec.fieldContext_LikeNotificationsResponse_notifications(ctx, field) + case "totalCount": + return ec.fieldContext_LikeNotificationsResponse_totalCount(ctx, field) + case "unreadCount": + return ec.fieldContext_LikeNotificationsResponse_unreadCount(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type LikeNotificationsResponse", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_getLikeNotifications_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query___type(ctx, field) if err != nil { @@ -11720,6 +12726,69 @@ func (ec *executionContext) fieldContext_User_subscriptionNotifications(ctx cont return fc, nil } +func (ec *executionContext) _User_likeNotifications(ctx context.Context, field graphql.CollectedField, obj *domain.User) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_User_likeNotifications(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.User().LikeNotifications(rctx, obj, fc.Args["unreadOnly"].(*bool), fc.Args["limit"].(*int), fc.Args["offset"].(*int)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*LikeNotificationsResponse) + fc.Result = res + return ec.marshalNLikeNotificationsResponse2ᚖtailly_back_v2ᚋinternalᚋhttpᚋgraphᚐLikeNotificationsResponse(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_User_likeNotifications(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "User", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "notifications": + return ec.fieldContext_LikeNotificationsResponse_notifications(ctx, field) + case "totalCount": + return ec.fieldContext_LikeNotificationsResponse_totalCount(ctx, field) + case "unreadCount": + return ec.fieldContext_LikeNotificationsResponse_unreadCount(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type LikeNotificationsResponse", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_User_likeNotifications_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) ___Directive_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { fc, err := ec.fieldContext___Directive_name(ctx, field) if err != nil { @@ -14969,6 +16038,227 @@ func (ec *executionContext) _Like(ctx context.Context, sel ast.SelectionSet, obj } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "isRead": + out.Values[i] = ec._Like_isRead(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "notifiedAt": + field := field + + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Like_notifiedAt(ctx, field, obj) + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var likeNotificationImplementors = []string{"LikeNotification"} + +func (ec *executionContext) _LikeNotification(ctx context.Context, sel ast.SelectionSet, obj *domain.LikeNotification) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, likeNotificationImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("LikeNotification") + case "id": + out.Values[i] = ec._LikeNotification_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "liker": + out.Values[i] = ec._LikeNotification_liker(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "post": + out.Values[i] = ec._LikeNotification_post(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "isRead": + out.Values[i] = ec._LikeNotification_isRead(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "createdAt": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._LikeNotification_createdAt(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var likeNotificationsResponseImplementors = []string{"LikeNotificationsResponse"} + +func (ec *executionContext) _LikeNotificationsResponse(ctx context.Context, sel ast.SelectionSet, obj *LikeNotificationsResponse) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, likeNotificationsResponseImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("LikeNotificationsResponse") + case "notifications": + out.Values[i] = ec._LikeNotificationsResponse_notifications(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "totalCount": + out.Values[i] = ec._LikeNotificationsResponse_totalCount(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "unreadCount": + out.Values[i] = ec._LikeNotificationsResponse_unreadCount(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var markLikeNotificationReadResultImplementors = []string{"MarkLikeNotificationReadResult"} + +func (ec *executionContext) _MarkLikeNotificationReadResult(ctx context.Context, sel ast.SelectionSet, obj *MarkLikeNotificationReadResult) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, markLikeNotificationReadResultImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("MarkLikeNotificationReadResult") + case "success": + out.Values[i] = ec._MarkLikeNotificationReadResult_success(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "message": + out.Values[i] = ec._MarkLikeNotificationReadResult_message(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -15375,6 +16665,20 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { out.Invalids++ } + case "markLikeNotificationAsRead": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_markLikeNotificationAsRead(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "markAllLikeNotificationsAsRead": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_markAllLikeNotificationsAsRead(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -16294,6 +17598,28 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "getLikeNotifications": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_getLikeNotifications(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "__type": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { @@ -17058,6 +18384,42 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj continue } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "likeNotifications": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._User_likeNotifications(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) default: panic("unknown field " + strconv.Quote(field.Name)) @@ -17961,11 +19323,93 @@ func (ec *executionContext) marshalNLike2ᚖtailly_back_v2ᚋinternalᚋdomain return ec._Like(ctx, sel, v) } +func (ec *executionContext) marshalNLikeNotification2ᚕᚖtailly_back_v2ᚋinternalᚋdomainᚐLikeNotificationᚄ(ctx context.Context, sel ast.SelectionSet, v []*domain.LikeNotification) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNLikeNotification2ᚖtailly_back_v2ᚋinternalᚋdomainᚐLikeNotification(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNLikeNotification2ᚖtailly_back_v2ᚋinternalᚋdomainᚐLikeNotification(ctx context.Context, sel ast.SelectionSet, v *domain.LikeNotification) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._LikeNotification(ctx, sel, v) +} + +func (ec *executionContext) marshalNLikeNotificationsResponse2tailly_back_v2ᚋinternalᚋhttpᚋgraphᚐLikeNotificationsResponse(ctx context.Context, sel ast.SelectionSet, v LikeNotificationsResponse) graphql.Marshaler { + return ec._LikeNotificationsResponse(ctx, sel, &v) +} + +func (ec *executionContext) marshalNLikeNotificationsResponse2ᚖtailly_back_v2ᚋinternalᚋhttpᚋgraphᚐLikeNotificationsResponse(ctx context.Context, sel ast.SelectionSet, v *LikeNotificationsResponse) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._LikeNotificationsResponse(ctx, sel, v) +} + func (ec *executionContext) unmarshalNLoginInput2tailly_back_v2ᚋinternalᚋdomainᚐLoginInput(ctx context.Context, v any) (domain.LoginInput, error) { res, err := ec.unmarshalInputLoginInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) marshalNMarkLikeNotificationReadResult2tailly_back_v2ᚋinternalᚋhttpᚋgraphᚐMarkLikeNotificationReadResult(ctx context.Context, sel ast.SelectionSet, v MarkLikeNotificationReadResult) graphql.Marshaler { + return ec._MarkLikeNotificationReadResult(ctx, sel, &v) +} + +func (ec *executionContext) marshalNMarkLikeNotificationReadResult2ᚖtailly_back_v2ᚋinternalᚋhttpᚋgraphᚐMarkLikeNotificationReadResult(ctx context.Context, sel ast.SelectionSet, v *MarkLikeNotificationReadResult) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._MarkLikeNotificationReadResult(ctx, sel, v) +} + func (ec *executionContext) marshalNMarkNotificationReadResult2tailly_back_v2ᚋinternalᚋhttpᚋgraphᚐMarkNotificationReadResult(ctx context.Context, sel ast.SelectionSet, v MarkNotificationReadResult) graphql.Marshaler { return ec._MarkNotificationReadResult(ctx, sel, &v) } diff --git a/internal/http/graph/like_notification_resolvers.go b/internal/http/graph/like_notification_resolvers.go new file mode 100644 index 0000000..ed0a804 --- /dev/null +++ b/internal/http/graph/like_notification_resolvers.go @@ -0,0 +1,105 @@ +package graph + +import ( + "context" + "fmt" +) + +// GetLikeNotifications is the resolver for the getLikeNotifications field. +func (r *queryResolver) GetLikeNotifications(ctx context.Context, unreadOnly *bool, limit *int, offset *int) (*LikeNotificationsResponse, error) { + userID, err := getUserIDFromContext(ctx) + if err != nil { + return nil, fmt.Errorf("authentication required: %w", err) + } + + unreadOnlyVal := false + if unreadOnly != nil { + unreadOnlyVal = *unreadOnly + } + + limitVal := 20 + if limit != nil { + limitVal = *limit + } + + offsetVal := 0 + if offset != nil { + offsetVal = *offset + } + + notifications, totalCount, unreadCount, err := r.Services.Like.GetLikeNotifications(ctx, userID, unreadOnlyVal, limitVal, offsetVal) + if err != nil { + return nil, fmt.Errorf("failed to get like notifications: %w", err) + } + + // Заполняем дополнительные поля для GraphQL + for _, notification := range notifications { + // Получаем информацию о пользователе, который поставил лайк + likerUser, err := r.Services.User.GetByID(ctx, notification.LikerID) + if err != nil { + return nil, fmt.Errorf("failed to get liker user: %w", err) + } + notification.SetLiker(likerUser) + + // Получаем информацию о посте + post, err := r.Services.Post.GetByID(ctx, notification.PostID) + if err != nil { + return nil, fmt.Errorf("failed to get post: %w", err) + } + + // Получаем автора поста + postAuthor, err := r.Services.User.GetByID(ctx, post.AuthorID) + if err != nil { + return nil, fmt.Errorf("failed to get post author: %w", err) + } + + // Устанавливаем автора поста + post.AuthorID = postAuthor.ID + notification.SetPost(post) + + // Форматируем дату + notification.SetCreatedAtStr() + } + + return &LikeNotificationsResponse{ + Notifications: notifications, + TotalCount: totalCount, + UnreadCount: unreadCount, + }, nil +} + +// MarkLikeNotificationAsRead is the resolver for the markLikeNotificationAsRead field. +func (r *mutationResolver) MarkLikeNotificationAsRead(ctx context.Context, notificationID int) (*MarkLikeNotificationReadResult, error) { + userID, err := getUserIDFromContext(ctx) + if err != nil { + return nil, fmt.Errorf("authentication required: %w", err) + } + + err = r.Services.Like.MarkLikeNotificationAsRead(ctx, notificationID, userID) + if err != nil { + return nil, fmt.Errorf("failed to mark notification as read: %w", err) + } + + return &MarkLikeNotificationReadResult{ + Success: true, + Message: "Notification marked as read successfully", + }, nil +} + +// MarkAllLikeNotificationsAsRead is the resolver for the markAllLikeNotificationsAsRead field. +func (r *mutationResolver) MarkAllLikeNotificationsAsRead(ctx context.Context) (*MarkLikeNotificationReadResult, error) { + userID, err := getUserIDFromContext(ctx) + if err != nil { + return nil, fmt.Errorf("authentication required: %w", err) + } + + err = r.Services.Like.MarkAllLikeNotificationsAsRead(ctx, userID) + if err != nil { + return nil, fmt.Errorf("failed to mark all notifications as read: %w", err) + } + + return &MarkLikeNotificationReadResult{ + Success: true, + Message: "All notifications marked as read successfully", + }, nil +} diff --git a/internal/http/graph/like_resolvers.go b/internal/http/graph/like_resolvers.go index b6c1398..58c52da 100644 --- a/internal/http/graph/like_resolvers.go +++ b/internal/http/graph/like_resolvers.go @@ -23,12 +23,28 @@ func (r *likeResolver) Post(ctx context.Context, obj *domain.Like) (*domain.Post // User is the resolver for the user field. func (r *likeResolver) User(ctx context.Context, obj *domain.Like) (*domain.User, error) { - // This would typically use a UserService to fetch the user - // For now, we'll return nil as the user service isn't shown in the provided code - return nil, nil + user, err := r.Services.User.GetByID(ctx, obj.UserID) + if err != nil { + return nil, fmt.Errorf("failed to get user: %w", err) + } + return user, nil } // CreatedAt is the resolver for the createdAt field. func (r *likeResolver) CreatedAt(ctx context.Context, obj *domain.Like) (string, error) { return obj.CreatedAt.Format(time.RFC3339), nil } + +// IsRead is the resolver for the isRead field. +func (r *likeResolver) IsRead(ctx context.Context, obj *domain.Like) (bool, error) { + return obj.IsRead, nil +} + +// NotifiedAt is the resolver for the notifiedAt field. +func (r *likeResolver) NotifiedAt(ctx context.Context, obj *domain.Like) (*string, error) { + if obj.NotifiedAt.IsZero() { + return nil, nil + } + formatted := obj.NotifiedAt.Format(time.RFC3339) + return &formatted, nil +} diff --git a/internal/http/graph/models_gen.go b/internal/http/graph/models_gen.go index 03e4f88..6f45b99 100644 --- a/internal/http/graph/models_gen.go +++ b/internal/http/graph/models_gen.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "strconv" + "tailly_back_v2/internal/domain" ) type FollowResult struct { @@ -38,6 +39,17 @@ type FollowingResponse struct { TotalCount int `json:"totalCount"` } +type LikeNotificationsResponse struct { + Notifications []*domain.LikeNotification `json:"notifications"` + TotalCount int `json:"totalCount"` + UnreadCount int `json:"unreadCount"` +} + +type MarkLikeNotificationReadResult struct { + Success bool `json:"success"` + Message string `json:"message"` +} + type MarkNotificationReadResult struct { Success bool `json:"success"` Message string `json:"message"` diff --git a/internal/http/graph/schema.graphql b/internal/http/graph/schema.graphql index bc99ef5..313273a 100644 --- a/internal/http/graph/schema.graphql +++ b/internal/http/graph/schema.graphql @@ -17,6 +17,11 @@ type User { limit: Int = 20 offset: Int = 0 ): NotificationsResponse! + likeNotifications( + unreadOnly: Boolean = false + limit: Int = 20 + offset: Int = 0 + ): LikeNotificationsResponse! } # Пост в блоге @@ -45,10 +50,12 @@ type Comment { # Лайк к посту type Like { - id: Int! # Уникальный идентификатор - post: Post! # Пост, который лайкнули - user: User! # Пользователь, который поставил лайк - createdAt: String! # Дата создания + id: Int! # Уникальный идентификатор + post: Post! # Пост, который лайкнули + user: User! # Пользователь, который поставил лайк + createdAt: String! # Дата создания + isRead: Boolean! # Прочитано ли уведомление + notifiedAt: String # Когда было отправлено уведомление } type Tokens { @@ -170,7 +177,23 @@ type MarkNotificationReadResult { message: String! } +type LikeNotification { + id: Int! + liker: User! + post: Post! + isRead: Boolean! + createdAt: String! +} +type LikeNotificationsResponse { + notifications: [LikeNotification!]! + totalCount: Int! + unreadCount: Int! +} +type MarkLikeNotificationReadResult { + success: Boolean! + message: String! +} # Клип type Clip { id: Int! @@ -246,6 +269,12 @@ type Query { clipComments(clipId: Int!, limit: Int, offset: Int): [ClipComment!]! # Комментарии клипа clipLikes(clipId: Int!, limit: Int, offset: Int): [ClipLike!]! # Лайки клипа isLiked(clipId: Int!): Boolean! # Проверить лайк текущего пользователя + + getLikeNotifications( + unreadOnly: Boolean = false + limit: Int = 20 + offset: Int = 0 + ): LikeNotificationsResponse! } @@ -294,6 +323,8 @@ type Mutation { unlikeClip(clipId: Int!): Boolean! # Убрать лайк createClipComment(clipId: Int!, content: String!): ClipComment! # Создать комментарий deleteClipComment(commentId: Int!): Boolean! # Удалить комментарий + markLikeNotificationAsRead(notificationId: Int!): MarkLikeNotificationReadResult! + markAllLikeNotificationsAsRead: MarkLikeNotificationReadResult! } type Subscription { diff --git a/internal/http/graph/user_resolvers.go b/internal/http/graph/user_resolvers.go index 46c6e1a..f0e59b6 100644 --- a/internal/http/graph/user_resolvers.go +++ b/internal/http/graph/user_resolvers.go @@ -256,3 +256,71 @@ func (r *userResolver) SubscriptionNotifications(ctx context.Context, obj *domai UnreadCount: int(res.UnreadCount), }, nil } + +// LikeNotifications is the resolver for the likeNotifications field. +func (r *userResolver) LikeNotifications(ctx context.Context, obj *domain.User, unreadOnly *bool, limit *int, offset *int) (*LikeNotificationsResponse, error) { + currentUserID, err := getUserIDFromContext(ctx) + if err != nil { + return nil, fmt.Errorf("authentication required: %w", err) + } + + // Можно смотреть только свои уведомления + if obj.ID != currentUserID { + return nil, fmt.Errorf("access denied: can only view your own notifications") + } + + unreadOnlyVal := false + if unreadOnly != nil { + unreadOnlyVal = *unreadOnly + } + + limitVal := 20 + if limit != nil { + limitVal = *limit + } + + offsetVal := 0 + if offset != nil { + offsetVal = *offset + } + + notifications, totalCount, unreadCount, err := r.Services.Like.GetLikeNotifications(ctx, obj.ID, unreadOnlyVal, limitVal, offsetVal) + if err != nil { + return nil, fmt.Errorf("failed to get like notifications: %w", err) + } + + // Заполняем дополнительные поля для GraphQL + for _, notification := range notifications { + // Получаем информацию о пользователе, который поставил лайк + likerUser, err := r.Services.User.GetByID(ctx, notification.LikerID) + if err != nil { + return nil, fmt.Errorf("failed to get liker user: %w", err) + } + notification.SetLiker(likerUser) + + // Получаем информацию о посте + post, err := r.Services.Post.GetByID(ctx, notification.PostID) + if err != nil { + return nil, fmt.Errorf("failed to get post: %w", err) + } + + // Получаем автора поста + postAuthor, err := r.Services.User.GetByID(ctx, post.AuthorID) + if err != nil { + return nil, fmt.Errorf("failed to get post author: %w", err) + } + + // Устанавливаем автора поста + post.Author = postAuthor + notification.SetPost(post) + + // Форматируем дату + notification.SetCreatedAtStr() + } + + return &LikeNotificationsResponse{ + Notifications: notifications, + TotalCount: totalCount, + UnreadCount: unreadCount, + }, nil +} diff --git a/internal/repository/like_repository.go b/internal/repository/like_repository.go index 8b2f258..89b0520 100644 --- a/internal/repository/like_repository.go +++ b/internal/repository/like_repository.go @@ -19,22 +19,18 @@ type LikeRepository interface { GetByUserAndPost(ctx context.Context, userID, postID int) (*domain.Like, error) Delete(ctx context.Context, id int) error DeleteByUserAndPost(ctx context.Context, userID, postID int) error + // Новые методы для уведомлений + GetUnreadLikeNotifications(ctx context.Context, userID int, limit, offset int) ([]*domain.LikeNotification, error) + GetAllLikeNotifications(ctx context.Context, userID int, limit, offset int) ([]*domain.LikeNotification, error) + MarkLikeNotificationAsRead(ctx context.Context, notificationID, userID int) error + MarkAllLikeNotificationsAsRead(ctx context.Context, userID int) error + GetLikeNotificationCounts(ctx context.Context, userID int) (totalCount, unreadCount int, err error) } type likeRepository struct { db *sql.DB } -func (r *likeRepository) GetByID(ctx context.Context, id int) (*domain.Like, error) { - //TODO implement me - panic("implement me") -} - -func (r *likeRepository) Delete(ctx context.Context, id int) error { - //TODO implement me - panic("implement me") -} - func NewLikeRepository(db *sql.DB) *likeRepository { return &likeRepository{db: db} } @@ -49,8 +45,8 @@ func (r *likeRepository) Create(ctx context.Context, like *domain.Like) error { } query := ` - INSERT INTO likes (post_id, user_id, created_at) - VALUES ($1, $2, $3) + INSERT INTO likes (post_id, user_id, created_at, is_read, notified_at) + VALUES ($1, $2, $3, $4, $5) RETURNING id ` @@ -58,14 +54,43 @@ func (r *likeRepository) Create(ctx context.Context, like *domain.Like) error { like.PostID, like.UserID, like.CreatedAt, + like.IsRead, + like.NotifiedAt, ).Scan(&like.ID) return err } +func (r *likeRepository) GetByID(ctx context.Context, id int) (*domain.Like, error) { + query := ` + SELECT id, post_id, user_id, created_at, is_read, notified_at + FROM likes + WHERE id = $1 + ` + + like := &domain.Like{} + err := r.db.QueryRowContext(ctx, query, id).Scan( + &like.ID, + &like.PostID, + &like.UserID, + &like.CreatedAt, + &like.IsRead, + &like.NotifiedAt, + ) + + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrLikeNotFound + } + return nil, err + } + + return like, nil +} + func (r *likeRepository) GetByPostID(ctx context.Context, postID int) ([]*domain.Like, error) { query := ` - SELECT id, post_id, user_id, created_at + SELECT id, post_id, user_id, created_at, is_read, notified_at FROM likes WHERE post_id = $1 ORDER BY created_at DESC @@ -85,6 +110,8 @@ func (r *likeRepository) GetByPostID(ctx context.Context, postID int) ([]*domain &like.PostID, &like.UserID, &like.CreatedAt, + &like.IsRead, + &like.NotifiedAt, ) if err != nil { return nil, err @@ -95,18 +122,9 @@ func (r *likeRepository) GetByPostID(ctx context.Context, postID int) ([]*domain return likes, nil } -func (r *likeRepository) DeleteByUserAndPost(ctx context.Context, userID, postID int) error { - query := ` - DELETE FROM likes - WHERE user_id = $1 AND post_id = $2 - ` - - _, err := r.db.ExecContext(ctx, query, userID, postID) - return err -} func (r *likeRepository) GetByUserAndPost(ctx context.Context, userID, postID int) (*domain.Like, error) { query := ` - SELECT id, post_id, user_id, created_at + SELECT id, post_id, user_id, created_at, is_read, notified_at FROM likes WHERE user_id = $1 AND post_id = $2 LIMIT 1 @@ -118,6 +136,8 @@ func (r *likeRepository) GetByUserAndPost(ctx context.Context, userID, postID in &like.PostID, &like.UserID, &like.CreatedAt, + &like.IsRead, + &like.NotifiedAt, ) if err != nil { @@ -129,3 +149,166 @@ func (r *likeRepository) GetByUserAndPost(ctx context.Context, userID, postID in return like, nil } + +func (r *likeRepository) Delete(ctx context.Context, id int) error { + query := `DELETE FROM likes WHERE id = $1` + _, err := r.db.ExecContext(ctx, query, id) + return err +} + +func (r *likeRepository) DeleteByUserAndPost(ctx context.Context, userID, postID int) error { + query := `DELETE FROM likes WHERE user_id = $1 AND post_id = $2` + _, err := r.db.ExecContext(ctx, query, userID, postID) + return err +} + +// Новые методы для уведомлений + +func (r *likeRepository) GetUnreadLikeNotifications(ctx context.Context, userID int, limit, offset int) ([]*domain.LikeNotification, error) { + query := ` + SELECT l.id, l.post_id, l.user_id as liker_id, l.created_at, l.is_read, + u.username as liker_username, u.avatar as liker_avatar, + p.title as post_title, p.author_id as post_author_id + FROM likes l + JOIN users u ON l.user_id = u.id + JOIN posts p ON l.post_id = p.id + WHERE p.author_id = $1 AND l.is_read = false + ORDER BY l.created_at DESC + LIMIT $2 OFFSET $3 + ` + + rows, err := r.db.QueryContext(ctx, query, userID, limit, offset) + if err != nil { + return nil, err + } + defer rows.Close() + + var notifications []*domain.LikeNotification + for rows.Next() { + notification := &domain.LikeNotification{} + err := rows.Scan( + ¬ification.ID, + ¬ification.PostID, + ¬ification.LikerID, + ¬ification.CreatedAt, + ¬ification.IsRead, + ¬ification.LikerUsername, + ¬ification.LikerAvatar, + ¬ification.PostTitle, + ¬ification.PostAuthorID, + ) + if err != nil { + return nil, err + } + notifications = append(notifications, notification) + } + + return notifications, nil +} + +func (r *likeRepository) GetAllLikeNotifications(ctx context.Context, userID int, limit, offset int) ([]*domain.LikeNotification, error) { + query := ` + SELECT l.id, l.post_id, l.user_id as liker_id, l.created_at, l.is_read, + u.username as liker_username, u.avatar as liker_avatar, + p.title as post_title, p.author_id as post_author_id + FROM likes l + JOIN users u ON l.user_id = u.id + JOIN posts p ON l.post_id = p.id + WHERE p.author_id = $1 + ORDER BY l.created_at DESC + LIMIT $2 OFFSET $3 + ` + + rows, err := r.db.QueryContext(ctx, query, userID, limit, offset) + if err != nil { + return nil, err + } + defer rows.Close() + + var notifications []*domain.LikeNotification + for rows.Next() { + notification := &domain.LikeNotification{} + err := rows.Scan( + ¬ification.ID, + ¬ification.PostID, + ¬ification.LikerID, + ¬ification.CreatedAt, + ¬ification.IsRead, + ¬ification.LikerUsername, + ¬ification.LikerAvatar, + ¬ification.PostTitle, + ¬ification.PostAuthorID, + ) + if err != nil { + return nil, err + } + notifications = append(notifications, notification) + } + + return notifications, nil +} + +func (r *likeRepository) MarkLikeNotificationAsRead(ctx context.Context, notificationID, userID int) error { + query := ` + UPDATE likes + SET is_read = true + WHERE id = $1 AND post_id IN ( + SELECT id FROM posts WHERE author_id = $2 + ) + ` + result, err := r.db.ExecContext(ctx, query, notificationID, userID) + if err != nil { + return err + } + + rowsAffected, err := result.RowsAffected() + if err != nil { + return err + } + + if rowsAffected == 0 { + return ErrLikeNotFound + } + + return nil +} + +func (r *likeRepository) MarkAllLikeNotificationsAsRead(ctx context.Context, userID int) error { + query := ` + UPDATE likes + SET is_read = true + WHERE post_id IN ( + SELECT id FROM posts WHERE author_id = $1 + ) AND is_read = false + ` + _, err := r.db.ExecContext(ctx, query, userID) + return err +} + +func (r *likeRepository) GetLikeNotificationCounts(ctx context.Context, userID int) (totalCount, unreadCount int, err error) { + queryTotal := ` + SELECT COUNT(*) + FROM likes l + JOIN posts p ON l.post_id = p.id + WHERE p.author_id = $1 + ` + + queryUnread := ` + SELECT COUNT(*) + FROM likes l + JOIN posts p ON l.post_id = p.id + WHERE p.author_id = $1 AND l.is_read = false + ` + + err = r.db.QueryRowContext(ctx, queryTotal, userID).Scan(&totalCount) + if err != nil { + return 0, 0, err + } + + err = r.db.QueryRowContext(ctx, queryUnread, userID).Scan(&unreadCount) + if err != nil { + return 0, 0, err + } + + return totalCount, unreadCount, nil +} diff --git a/internal/service/like_service.go b/internal/service/like_service.go index 9371fb4..4d8ba20 100644 --- a/internal/service/like_service.go +++ b/internal/service/like_service.go @@ -15,26 +15,33 @@ type LikeService interface { GetByPostID(ctx context.Context, postID int) ([]*domain.Like, error) GetCountForPost(ctx context.Context, postID int) (int, error) CheckIfLiked(ctx context.Context, userID, postID int) (bool, error) + // Новые методы для уведомлений + GetLikeNotifications(ctx context.Context, userID int, unreadOnly bool, limit, offset int) ([]*domain.LikeNotification, int, int, error) + MarkLikeNotificationAsRead(ctx context.Context, notificationID, userID int) error + MarkAllLikeNotificationsAsRead(ctx context.Context, userID int) error } // Реализация сервиса лайков type likeService struct { likeRepo repository.LikeRepository - postRepo repository.PostRepository // Для проверки существования поста + postRepo repository.PostRepository + userRepo repository.UserRepository // Добавляем для получения информации о пользователях } // Конструктор сервиса -func NewLikeService(likeRepo repository.LikeRepository, postRepo repository.PostRepository) LikeService { +func NewLikeService(likeRepo repository.LikeRepository, postRepo repository.PostRepository, userRepo repository.UserRepository) LikeService { return &likeService{ likeRepo: likeRepo, postRepo: postRepo, + userRepo: userRepo, } } // Поставить лайк посту func (s *likeService) LikePost(ctx context.Context, userID, postID int) (*domain.Like, error) { // Проверяем существование поста - if _, err := s.postRepo.GetByID(ctx, postID); err != nil { + post, err := s.postRepo.GetByID(ctx, postID) + if err != nil { if errors.Is(err, repository.ErrPostNotFound) { return nil, errors.New("post not found") } @@ -48,10 +55,17 @@ func (s *likeService) LikePost(ctx context.Context, userID, postID int) (*domain return nil, errors.New("you have already liked this post") } + // Проверяем, что пользователь не лайкает свой собственный пост + if post.AuthorID == userID { + return nil, errors.New("cannot like your own post") + } + like := &domain.Like{ - PostID: postID, - UserID: userID, - CreatedAt: time.Now(), + PostID: postID, + UserID: userID, + CreatedAt: time.Now(), + IsRead: false, + NotifiedAt: time.Now(), } if err := s.likeRepo.Create(ctx, like); err != nil { @@ -96,7 +110,6 @@ func (s *likeService) GetByPostID(ctx context.Context, postID int) ([]*domain.Li return nil, err } - // Возвращаем пустой слайс вместо nil if likes == nil { return []*domain.Like{}, nil } @@ -124,3 +137,35 @@ func (s *likeService) CheckIfLiked(ctx context.Context, userID, postID int) (boo } return true, nil } + +// Новые методы для уведомлений + +func (s *likeService) GetLikeNotifications(ctx context.Context, userID int, unreadOnly bool, limit, offset int) ([]*domain.LikeNotification, int, int, error) { + var notifications []*domain.LikeNotification + var err error + + if unreadOnly { + notifications, err = s.likeRepo.GetUnreadLikeNotifications(ctx, userID, limit, offset) + } else { + notifications, err = s.likeRepo.GetAllLikeNotifications(ctx, userID, limit, offset) + } + + if err != nil { + return nil, 0, 0, err + } + + totalCount, unreadCount, err := s.likeRepo.GetLikeNotificationCounts(ctx, userID) + if err != nil { + return nil, 0, 0, err + } + + return notifications, totalCount, unreadCount, nil +} + +func (s *likeService) MarkLikeNotificationAsRead(ctx context.Context, notificationID, userID int) error { + return s.likeRepo.MarkLikeNotificationAsRead(ctx, notificationID, userID) +} + +func (s *likeService) MarkAllLikeNotificationsAsRead(ctx context.Context, userID int) error { + return s.likeRepo.MarkAllLikeNotificationsAsRead(ctx, userID) +} diff --git a/migrations/0003_initial_schema.up.sql b/migrations/0003_initial_schema.up.sql new file mode 100644 index 0000000..1e785a3 --- /dev/null +++ b/migrations/0003_initial_schema.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE likes ADD COLUMN is_read BOOLEAN NOT NULL DEFAULT FALSE; +ALTER TABLE likes ADD COLUMN notified_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(); \ No newline at end of file