Use packed notification flags

This commit is contained in:
Vyr Cossont 2024-12-25 17:40:23 -08:00
parent 86c686b5d6
commit 4bbd76abcf
8 changed files with 109 additions and 129 deletions

View file

@ -56,6 +56,7 @@ type Notification struct {
NotificationPendingReply NotificationType = 10 // NotificationPendingReply -- Someone has replied to a status of yours, which requires approval by you.
NotificationPendingReblog NotificationType = 11 // NotificationPendingReblog -- Someone has boosted a status of yours, which requires approval by you.
NotificationAdminReport NotificationType = 12 // NotificationAdminReport -- someone has submitted a new report to the instance.
NotificationTypeNumValues NotificationType = 13 // NotificationTypeNumValues -- 1 + number of max notification type
)
// String returns a stringified, frontend API compatible form of NotificationType.

View file

@ -49,19 +49,44 @@ type WebPushSubscription struct {
// P256dh is a Base64-encoded Diffie-Hellman public key on the P-256 elliptic curve.
P256dh string `bun:",nullzero,notnull"`
// NotifyFollow and friends control which notifications are delivered to a given subscription.
// Corresponds to NotificationType and model.PushSubscriptionAlerts.
NotifyFollow *bool `bun:",nullzero,notnull,default:false"`
NotifyFollowRequest *bool `bun:",nullzero,notnull,default:false"`
NotifyFavourite *bool `bun:",nullzero,notnull,default:false"`
NotifyMention *bool `bun:",nullzero,notnull,default:false"`
NotifyReblog *bool `bun:",nullzero,notnull,default:false"`
NotifyPoll *bool `bun:",nullzero,notnull,default:false"`
NotifyStatus *bool `bun:",nullzero,notnull,default:false"`
NotifyUpdate *bool `bun:",nullzero,notnull,default:false"`
NotifyAdminSignup *bool `bun:",nullzero,notnull,default:false"`
NotifyAdminReport *bool `bun:",nullzero,notnull,default:false"`
NotifyPendingFavourite *bool `bun:",nullzero,notnull,default:false"`
NotifyPendingReply *bool `bun:",nullzero,notnull,default:false"`
NotifyPendingReblog *bool `bun:",nullzero,notnull,default:false"`
// NotificationFlags controls which notifications are delivered to a given subscription.
// Corresponds to model.PushSubscriptionAlerts.
NotificationFlags WebPushSubscriptionNotificationFlags `bun:",notnull"`
}
// WebPushSubscriptionNotificationFlags is a bitfield representation of a set of NotificationType.
type WebPushSubscriptionNotificationFlags int64
// WebPushSubscriptionNotificationFlagsFromSlice packs a slice of NotificationType into a WebPushSubscriptionNotificationFlags.
func WebPushSubscriptionNotificationFlagsFromSlice(notificationTypes []NotificationType) WebPushSubscriptionNotificationFlags {
var n WebPushSubscriptionNotificationFlags
for _, notificationType := range notificationTypes {
n.Set(notificationType, true)
}
return n
}
// ToSlice unpacks a WebPushSubscriptionNotificationFlags into a slice of NotificationType.
func (n *WebPushSubscriptionNotificationFlags) ToSlice() []NotificationType {
notificationTypes := make([]NotificationType, 0, NotificationTypeNumValues)
for notificationType := NotificationUnknown; notificationType < NotificationTypeNumValues; notificationType++ {
if n.Get(notificationType) {
notificationTypes = append(notificationTypes, notificationType)
}
}
return notificationTypes
}
// Get tests to see if a given NotificationType is included in this set of flags.
func (n *WebPushSubscriptionNotificationFlags) Get(notificationType NotificationType) bool {
return *n&(1<<notificationType) != 0
}
// Set adds or removes a given NotificationType to or from this set of flags.
func (n *WebPushSubscriptionNotificationFlags) Set(notificationType NotificationType, value bool) {
if value {
*n |= 1 << notificationType
} else {
*n &= ^(1 << notificationType)
}
}

View file

@ -54,20 +54,9 @@ func (p *Processor) CreateOrReplace(
Endpoint: request.Subscription.Endpoint,
Auth: request.Subscription.Keys.Auth,
P256dh: request.Subscription.Keys.P256dh,
NotifyFollow: &request.Data.Alerts.Follow,
NotifyFollowRequest: &request.Data.Alerts.FollowRequest,
NotifyFavourite: &request.Data.Alerts.Favourite,
NotifyMention: &request.Data.Alerts.Mention,
NotifyReblog: &request.Data.Alerts.Reblog,
NotifyPoll: &request.Data.Alerts.Poll,
NotifyStatus: &request.Data.Alerts.Status,
NotifyUpdate: &request.Data.Alerts.Update,
NotifyAdminSignup: &request.Data.Alerts.AdminSignup,
NotifyAdminReport: &request.Data.Alerts.AdminReport,
NotifyPendingFavourite: &request.Data.Alerts.PendingFavourite,
NotifyPendingReply: &request.Data.Alerts.PendingReply,
NotifyPendingReblog: &request.Data.Alerts.PendingReblog,
NotificationFlags: alertsToNotificationFlags(request.Data.Alerts),
}
if err := p.state.DB.PutWebPushSubscription(ctx, subscription); err != nil {
return nil, gtserror.NewErrorInternalError(
gtserror.Newf("couldn't create Web Push subscription for token ID %s: %w", tokenID, err),

View file

@ -64,3 +64,25 @@ func (p *Processor) apiSubscription(ctx context.Context, subscription *gtsmodel.
return apiSubscription, nil
}
// alertsToNotificationFlags turns the alerts section of a push subscription API request into a packed bitfield.
func alertsToNotificationFlags(alerts *apimodel.WebPushSubscriptionAlerts) gtsmodel.WebPushSubscriptionNotificationFlags {
var n gtsmodel.WebPushSubscriptionNotificationFlags
n.Set(gtsmodel.NotificationFollow, alerts.Follow)
n.Set(gtsmodel.NotificationFollowRequest, alerts.FollowRequest)
n.Set(gtsmodel.NotificationFavourite, alerts.Favourite)
n.Set(gtsmodel.NotificationMention, alerts.Mention)
n.Set(gtsmodel.NotificationReblog, alerts.Reblog)
n.Set(gtsmodel.NotificationPoll, alerts.Poll)
n.Set(gtsmodel.NotificationStatus, alerts.Status)
// TODO: (Vyr) handle NotificationUpdate when edit patch is merged
//n.Set(gtsmodel.NotificationUpdate, alerts.Update)
n.Set(gtsmodel.NotificationAdminSignup, alerts.AdminSignup)
n.Set(gtsmodel.NotificationAdminReport, alerts.AdminReport)
n.Set(gtsmodel.NotificationPendingFave, alerts.PendingFavourite)
n.Set(gtsmodel.NotificationPendingReply, alerts.PendingReply)
n.Set(gtsmodel.NotificationPendingReblog, alerts.PendingReblog)
return n
}

View file

@ -49,35 +49,11 @@ func (p *Processor) Update(
}
// Update it.
subscription.NotifyFollow = &request.Data.Alerts.Follow
subscription.NotifyFollowRequest = &request.Data.Alerts.FollowRequest
subscription.NotifyFavourite = &request.Data.Alerts.Favourite
subscription.NotifyMention = &request.Data.Alerts.Mention
subscription.NotifyReblog = &request.Data.Alerts.Reblog
subscription.NotifyPoll = &request.Data.Alerts.Poll
subscription.NotifyStatus = &request.Data.Alerts.Status
subscription.NotifyUpdate = &request.Data.Alerts.Update
subscription.NotifyAdminSignup = &request.Data.Alerts.AdminSignup
subscription.NotifyAdminReport = &request.Data.Alerts.AdminReport
subscription.NotifyPendingFavourite = &request.Data.Alerts.PendingFavourite
subscription.NotifyPendingReply = &request.Data.Alerts.PendingReply
subscription.NotifyPendingReblog = &request.Data.Alerts.PendingReblog
subscription.NotificationFlags = alertsToNotificationFlags(request.Data.Alerts)
if err = p.state.DB.UpdateWebPushSubscription(
ctx,
subscription,
"notify_follow",
"notify_follow_request",
"notify_favourite",
"notify_mention",
"notify_reblog",
"notify_poll",
"notify_status",
"notify_update",
"notify_admin_signup",
"notify_admin_report",
"notify_pending_favourite",
"notify_pending_reply",
"notify_pending_reblog",
"notification_flags",
); err != nil {
return nil, gtserror.NewErrorInternalError(
gtserror.Newf("couldn't update Web Push subscription for token ID %s: %w", tokenID, err),

View file

@ -2831,19 +2831,19 @@ func (c *Converter) WebPushSubscriptionToAPIWebPushSubscription(
Endpoint: subscription.Endpoint,
ServerKey: vapidKeyPair.Public,
Alerts: apimodel.WebPushSubscriptionAlerts{
Follow: *subscription.NotifyFollow,
FollowRequest: *subscription.NotifyFollowRequest,
Favourite: *subscription.NotifyFavourite,
Mention: *subscription.NotifyMention,
Reblog: *subscription.NotifyReblog,
Poll: *subscription.NotifyPoll,
Status: *subscription.NotifyStatus,
Update: *subscription.NotifyUpdate,
AdminSignup: *subscription.NotifyAdminSignup,
AdminReport: *subscription.NotifyAdminReport,
PendingFavourite: *subscription.NotifyPendingFavourite,
PendingReply: *subscription.NotifyPendingReply,
PendingReblog: *subscription.NotifyPendingReblog,
Follow: subscription.NotificationFlags.Get(gtsmodel.NotificationFollow),
FollowRequest: subscription.NotificationFlags.Get(gtsmodel.NotificationFollowRequest),
Favourite: subscription.NotificationFlags.Get(gtsmodel.NotificationFavourite),
Mention: subscription.NotificationFlags.Get(gtsmodel.NotificationMention),
Reblog: subscription.NotificationFlags.Get(gtsmodel.NotificationReblog),
Poll: subscription.NotificationFlags.Get(gtsmodel.NotificationPoll),
Status: subscription.NotificationFlags.Get(gtsmodel.NotificationStatus),
Update: false, // TODO: (Vyr) handle NotificationUpdate when edit patch is merged
AdminSignup: subscription.NotificationFlags.Get(gtsmodel.NotificationAdminSignup),
AdminReport: subscription.NotificationFlags.Get(gtsmodel.NotificationAdminReport),
PendingFavourite: subscription.NotificationFlags.Get(gtsmodel.NotificationPendingFave),
PendingReply: subscription.NotificationFlags.Get(gtsmodel.NotificationPendingReply),
PendingReblog: subscription.NotificationFlags.Get(gtsmodel.NotificationPendingReblog),
},
}, nil
}

View file

@ -85,45 +85,10 @@ func (r *realSender) Send(
relevantSubscriptions := make([]*gtsmodel.WebPushSubscription, 0, len(subscriptions))
for _, subscription := range subscriptions {
// Check whether this subscription wants this type of notification.
notify := false
switch notification.NotificationType {
case gtsmodel.NotificationFollow:
notify = *subscription.NotifyFollow
case gtsmodel.NotificationFollowRequest:
notify = *subscription.NotifyFollowRequest
case gtsmodel.NotificationMention:
notify = *subscription.NotifyMention
case gtsmodel.NotificationReblog:
notify = *subscription.NotifyReblog
case gtsmodel.NotificationFavourite:
notify = *subscription.NotifyFavourite
case gtsmodel.NotificationPoll:
notify = *subscription.NotifyPoll
case gtsmodel.NotificationStatus:
notify = *subscription.NotifyStatus
case gtsmodel.NotificationAdminSignup:
notify = *subscription.NotifyAdminSignup
case gtsmodel.NotificationAdminReport:
notify = *subscription.NotifyAdminReport
case gtsmodel.NotificationPendingFave:
notify = *subscription.NotifyPendingFavourite
case gtsmodel.NotificationPendingReply:
notify = *subscription.NotifyPendingReply
case gtsmodel.NotificationPendingReblog:
notify = *subscription.NotifyPendingReblog
default:
log.Errorf(
ctx,
"notification type not supported by Web Push subscriptions: %v",
notification.NotificationType,
)
continue
}
if !notify {
continue
}
if subscription.NotificationFlags.Get(notification.NotificationType) {
relevantSubscriptions = append(relevantSubscriptions, subscription)
}
}
if len(relevantSubscriptions) == 0 {
return nil
}

View file

@ -3485,19 +3485,21 @@ func NewTestWebPushSubscriptions() map[string]*gtsmodel.WebPushSubscription {
Endpoint: "https://example.test/push",
Auth: "cgna/fzrYLDQyPf5hD7IsA==",
P256dh: "BMYVItYVOX+AHBdtA62Q0i6c+F7MV2Gia3aoDr8mvHkuPBNIOuTLDfmFcnBqoZcQk6BtLcIONbxhHpy2R+mYIUY=",
NotifyFollow: util.Ptr(true),
NotifyFollowRequest: util.Ptr(true),
NotifyFavourite: util.Ptr(true),
NotifyMention: util.Ptr(true),
NotifyReblog: util.Ptr(true),
NotifyPoll: util.Ptr(true),
NotifyStatus: util.Ptr(true),
NotifyUpdate: util.Ptr(true),
NotifyAdminSignup: util.Ptr(true),
NotifyAdminReport: util.Ptr(true),
NotifyPendingFavourite: util.Ptr(true),
NotifyPendingReply: util.Ptr(true),
NotifyPendingReblog: util.Ptr(true),
NotificationFlags: gtsmodel.WebPushSubscriptionNotificationFlagsFromSlice([]gtsmodel.NotificationType{
gtsmodel.NotificationFollow,
gtsmodel.NotificationFollowRequest,
gtsmodel.NotificationFavourite,
gtsmodel.NotificationMention,
gtsmodel.NotificationReblog,
gtsmodel.NotificationPoll,
gtsmodel.NotificationStatus,
// TODO: (Vyr) add NotificationUpdate when edit patch is merged
gtsmodel.NotificationAdminSignup,
gtsmodel.NotificationAdminReport,
gtsmodel.NotificationPendingFave,
gtsmodel.NotificationPendingReply,
gtsmodel.NotificationPendingReblog,
}),
},
}
}