diff --git a/db/gen/coredb/query.sql.go b/db/gen/coredb/query.sql.go index 764128d68..119d4fb2c 100644 --- a/db/gen/coredb/query.sql.go +++ b/db/gen/coredb/query.sql.go @@ -1214,6 +1214,54 @@ func (q *Queries) CreateUserEvent(ctx context.Context, arg CreateUserEventParams return i, err } +const createUserPostedYourWorkNotification = `-- name: CreateUserPostedYourWorkNotification :one +INSERT INTO notifications (id, owner_id, action, data, event_ids, post_id, contract_id) VALUES ($1, $2, $3, $4, $5, $7, $6) RETURNING id, deleted, owner_id, version, last_updated, created_at, action, data, event_ids, feed_event_id, comment_id, gallery_id, seen, amount, post_id, token_id, contract_id, mention_id +` + +type CreateUserPostedYourWorkNotificationParams struct { + ID persist.DBID `json:"id"` + OwnerID persist.DBID `json:"owner_id"` + Action persist.Action `json:"action"` + Data persist.NotificationData `json:"data"` + EventIds persist.DBIDList `json:"event_ids"` + ContractID persist.DBID `json:"contract_id"` + Post sql.NullString `json:"post"` +} + +func (q *Queries) CreateUserPostedYourWorkNotification(ctx context.Context, arg CreateUserPostedYourWorkNotificationParams) (Notification, error) { + row := q.db.QueryRow(ctx, createUserPostedYourWorkNotification, + arg.ID, + arg.OwnerID, + arg.Action, + arg.Data, + arg.EventIds, + arg.ContractID, + arg.Post, + ) + var i Notification + err := row.Scan( + &i.ID, + &i.Deleted, + &i.OwnerID, + &i.Version, + &i.LastUpdated, + &i.CreatedAt, + &i.Action, + &i.Data, + &i.EventIds, + &i.FeedEventID, + &i.CommentID, + &i.GalleryID, + &i.Seen, + &i.Amount, + &i.PostID, + &i.TokenID, + &i.ContractID, + &i.MentionID, + ) + return i, err +} + const createViewGalleryNotification = `-- name: CreateViewGalleryNotification :one INSERT INTO notifications (id, owner_id, action, data, event_ids, gallery_id) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, deleted, owner_id, version, last_updated, created_at, action, data, event_ids, feed_event_id, comment_id, gallery_id, seen, amount, post_id, token_id, contract_id, mention_id ` diff --git a/db/queries/core/query.sql b/db/queries/core/query.sql index ce83c8ef3..a806d8062 100644 --- a/db/queries/core/query.sql +++ b/db/queries/core/query.sql @@ -720,6 +720,9 @@ INSERT INTO notifications (id, owner_id, action, data, event_ids, feed_event_id, -- name: CreateContractNotification :one INSERT INTO notifications (id, owner_id, action, data, event_ids, feed_event_id, post_id, comment_id, contract_id, mention_id) VALUES ($1, $2, $3, $4, $5, sqlc.narg('feed_event'), sqlc.narg('post'), sqlc.narg('comment'), $6, $7) RETURNING *; +-- name: CreateUserPostedYourWorkNotification :one +INSERT INTO notifications (id, owner_id, action, data, event_ids, post_id, contract_id) VALUES ($1, $2, $3, $4, $5, sqlc.narg('post'), $6) RETURNING *; + -- name: CreateSimpleNotification :one INSERT INTO notifications (id, owner_id, action, data, event_ids) VALUES ($1, $2, $3, $4, $5) RETURNING *; diff --git a/event/event.go b/event/event.go index 05ed3e52d..95cc2bee0 100644 --- a/event/event.go +++ b/event/event.go @@ -66,6 +66,7 @@ func AddTo(ctx *gin.Context, disableDataloaderCaching bool, notif *notifications sender.addDelayedHandler(notifications, persist.ActionMentionUser, notificationHandler) sender.addDelayedHandler(notifications, persist.ActionMentionCommunity, notificationHandler) sender.addDelayedHandler(notifications, persist.ActionNewTokensReceived, notificationHandler) + sender.addDelayedHandler(notifications, persist.ActionUserPostedYourWork, notificationHandler) sender.feed = feed sender.notifications = notifications @@ -493,6 +494,8 @@ func (h notificationHandler) createNotificationDataForEvent(event db.Event) (dat data.NewTokenQuantity = event.Data.NewTokenQuantity case persist.ActionReplyToComment: data.OriginalCommentID = event.SubjectID + case persist.ActionUserPostedYourWork: + data.YourContractID = event.Data.YourContractID default: logger.For(nil).Debugf("no notification data for event: %s", event.Action) } @@ -555,6 +558,7 @@ func (h notificationHandler) findOwnerForNotificationFromEvent(ctx context.Conte return "", nil } return u.CreatorUserID, nil + } return "", fmt.Errorf("no owner found for event: %s", event.Action) diff --git a/publicapi/feed.go b/publicapi/feed.go index 794c6d09a..a453d7216 100644 --- a/publicapi/feed.go +++ b/publicapi/feed.go @@ -142,8 +142,8 @@ func (api FeedAPI) PostTokens(ctx context.Context, tokenIDs []persist.DBID, ment return "", err } - contractIDs, _ := util.Map(contracts, func(c db.Contract) (persist.DBID, error) { - return c.ID, nil + contractIDs := util.MapWithoutError(contracts, func(c db.Contract) persist.DBID { + return c.ID }) tx, err := api.repos.BeginTx(ctx) @@ -207,6 +207,25 @@ func (api FeedAPI) PostTokens(ctx context.Context, tokenIDs []persist.DBID, ment return "", err } + creators, _ := api.loaders.ContractCreatorByContractID.LoadAll(contractIDs) + for _, creator := range creators { + if creator.CreatorUserID == "" { + continue + } + err = event.Dispatch(ctx, db.Event{ + ActorID: persist.DBIDToNullStr(actorID), + Action: persist.ActionUserPostedYourWork, + ResourceTypeID: persist.ResourceTypeContract, + UserID: creator.CreatorUserID, + SubjectID: creator.ContractID, + PostID: postID, + ContractID: creator.ContractID, + }) + if err != nil { + logger.For(ctx).Errorf("error dispatching event: %v", err) + } + } + err = event.Dispatch(ctx, db.Event{ ActorID: persist.DBIDToNullStr(actorID), Action: persist.ActionUserPosted, @@ -273,6 +292,22 @@ func (api FeedAPI) ReferralPostToken(ctx context.Context, t persist.TokenIdentif return postID, err } + creator, _ := api.loaders.ContractCreatorByContractID.Load(contract.ID) + if creator.CreatorUserID != "" { + err = event.Dispatch(ctx, db.Event{ + ActorID: persist.DBIDToNullStr(userID), + Action: persist.ActionUserPostedYourWork, + ResourceTypeID: persist.ResourceTypeContract, + UserID: creator.CreatorUserID, + SubjectID: creator.ContractID, + PostID: postID, + ContractID: creator.ContractID, + }) + if err != nil { + logger.For(ctx).Errorf("error dispatching event: %v", err) + } + } + err = event.Dispatch(ctx, db.Event{ ActorID: persist.DBIDToNullStr(user.ID), Action: persist.ActionUserPosted, @@ -311,6 +346,22 @@ func (api FeedAPI) ReferralPostToken(ctx context.Context, t persist.TokenIdentif return postID, err } + creator, _ := api.loaders.ContractCreatorByContractID.Load(synced.Contract) + if creator.CreatorUserID != "" { + err = event.Dispatch(ctx, db.Event{ + ActorID: persist.DBIDToNullStr(userID), + Action: persist.ActionUserPostedYourWork, + ResourceTypeID: persist.ResourceTypeContract, + UserID: creator.CreatorUserID, + SubjectID: creator.ContractID, + PostID: postID, + ContractID: creator.ContractID, + }) + if err != nil { + logger.For(ctx).Errorf("error dispatching event: %v", err) + } + } + err = event.Dispatch(ctx, db.Event{ ActorID: persist.DBIDToNullStr(user.ID), Action: persist.ActionUserPosted, diff --git a/service/notifications/notifications.go b/service/notifications/notifications.go index 276dab813..4b697bc9a 100644 --- a/service/notifications/notifications.go +++ b/service/notifications/notifications.go @@ -163,6 +163,7 @@ func New(queries *db.Queries, pub *pubsub.Client, taskClient *cloudtasks.Client, notifDispatcher.AddHandler(persist.ActionReplyToComment, def) notifDispatcher.AddHandler(persist.ActionMentionUser, def) notifDispatcher.AddHandler(persist.ActionMentionCommunity, def) + notifDispatcher.AddHandler(persist.ActionUserPostedYourWork, def) // notification actions that are grouped by token id notifDispatcher.AddHandler(persist.ActionNewTokensReceived, tokenIDGrouped) @@ -723,6 +724,31 @@ func createPushMessage(ctx context.Context, notif db.Notification, queries *db.Q } + if notif.Action == persist.ActionUserPostedYourWork { + + post, err := queries.GetPostByID(ctx, notif.PostID) + if err != nil { + return task.PushNotificationMessage{}, err + } + actor, err := queries.GetUserById(ctx, post.ActorID) + if err != nil { + return task.PushNotificationMessage{}, err + } + + if err := limiter.tryMention(ctx, actor.ID, notif.OwnerID, notif.FeedEventID); err != nil { + return task.PushNotificationMessage{}, err + } + + if !actor.Username.Valid { + return task.PushNotificationMessage{}, fmt.Errorf("user with ID=%s has no username", actor.ID) + } + contract, err := queries.GetContractByID(ctx, notif.ContractID) + if err != nil { + return task.PushNotificationMessage{}, err + } + message.Body = fmt.Sprintf("%s posted your work: %s", actor.Username.String, contract.Name.String) + } + return task.PushNotificationMessage{}, fmt.Errorf("unsupported notification action: %s", notif.Action) } @@ -740,6 +766,8 @@ func actionSupportsPushNotifications(action persist.Action) bool { return true case persist.ActionAdmiredPost: return true + case persist.ActionUserPostedYourWork: + return true default: return false } @@ -972,6 +1000,16 @@ func addNotification(ctx context.Context, notif db.Notification, queries *db.Que MentionID: notif.MentionID, ContractID: notif.ContractID, }) + case persist.ActionUserPostedYourWork: + return queries.CreateUserPostedYourWorkNotification(ctx, db.CreateUserPostedYourWorkNotificationParams{ + ID: id, + OwnerID: notif.OwnerID, + Action: notif.Action, + Data: notif.Data, + EventIds: notif.EventIds, + Post: util.ToNullString(notif.PostID.String(), true), + ContractID: notif.ContractID, + }) default: return db.Notification{}, fmt.Errorf("unknown notification action: %s", notif.Action) } diff --git a/service/persist/event.go b/service/persist/event.go index 93ad22553..62dcb3c4d 100644 --- a/service/persist/event.go +++ b/service/persist/event.go @@ -21,6 +21,7 @@ const ( ActionUserCreated Action = "UserCreated" ActionUserFollowedUsers Action = "UserFollowedUsers" ActionUserPosted Action = "UserPosted" + ActionUserPostedYourWork Action = "UserPostedYourWork" ActionCollectorsNoteAddedToToken Action = "CollectorsNoteAddedToToken" ActionCollectionCreated Action = "CollectionCreated" ActionCollectorsNoteAddedToCollection Action = "CollectorsNoteAddedToCollection" @@ -58,6 +59,7 @@ type EventData struct { GalleryNewTokenIDs map[DBID]DBIDList `json:"gallery_new_token_ids"` GalleryNewCollections DBIDList `json:"gallery_new_collections"` GalleryNewTokenCollectorsNotes map[DBID]string `json:"gallery_new_token_collectors_notes"` + YourContractID DBID `json:"your_contract_id"` } type FeedEventData struct { diff --git a/service/persist/notification.go b/service/persist/notification.go index f74583eb4..4f0691613 100644 --- a/service/persist/notification.go +++ b/service/persist/notification.go @@ -14,6 +14,7 @@ type NotificationData struct { NewTokenID DBID `json:"new_token_id,omitempty"` NewTokenQuantity HexString `json:"new_token_quantity,omitempty"` OriginalCommentID DBID `json:"original_comment_id,omitempty"` + YourContractID DBID `json:"your_contract_id,omitempty"` } func (n NotificationData) Validate() NotificationData { @@ -25,6 +26,7 @@ func (n NotificationData) Validate() NotificationData { result.NewTokenID = n.NewTokenID result.NewTokenQuantity = n.NewTokenQuantity result.OriginalCommentID = n.OriginalCommentID + result.YourContractID = n.YourContractID return result } @@ -40,6 +42,7 @@ func (n NotificationData) Concat(other NotificationData) NotificationData { result.NewTokenQuantity = other.NewTokenQuantity.Add(n.NewTokenQuantity) result.NewTokenID = DBID(util.FirstNonEmptyString(other.NewTokenID.String(), n.NewTokenID.String())) result.OriginalCommentID = DBID(util.FirstNonEmptyString(other.OriginalCommentID.String(), n.OriginalCommentID.String())) + result.YourContractID = DBID(util.FirstNonEmptyString(other.YourContractID.String(), n.YourContractID.String())) return result.Validate() }