New Creator Notifications (#4383)

* Some new notification types

* Fix error

* Use existing DB models rather than inline queries

* Fix template fillout

* Fix ModerationThreadMessageReceived

* Insert more notifications, fix some formatting

* chore: query cache, clippy, fmt

* chore: query cache, clippy, fmt

* Use outer transactions to insert notifications instead of creating a new one

* Join futures
This commit is contained in:
François-Xavier Talbot
2025-09-17 15:37:21 -04:00
committed by GitHub
parent 8149618187
commit 6da190ed01
25 changed files with 1211 additions and 77 deletions

View File

@@ -96,6 +96,36 @@ pub enum LegacyNotificationBody {
amount: String,
service: String,
},
PatCreated {
token_name: String,
},
ModerationMessageReceived {
project_id: ProjectId,
},
ReportStatusUpdated {
report_id: ReportId,
},
ReportSubmitted {
report_id: ReportId,
},
ProjectStatusApproved {
project_id: ProjectId,
},
ProjectStatusNeutral {
project_id: ProjectId,
old_status: ProjectStatus,
new_status: ProjectStatus,
},
ProjectTransferred {
project_id: ProjectId,
// Store only the raw identifiers in legacy body
new_owner_user_id: Option<UserId>,
new_owner_organization_id: Option<OrganizationId>,
},
PayoutAvailable {
amount: f64,
date_available: DateTime<Utc>,
},
Unknown,
}
@@ -117,6 +147,27 @@ impl LegacyNotification {
NotificationBody::ModeratorMessage { .. } => {
Some("moderator_message".to_string())
}
NotificationBody::PatCreated { .. } => {
Some("pat_created".to_string())
}
NotificationBody::ModerationMessageReceived { .. } => {
Some("moderation_message_received".to_string())
}
NotificationBody::ReportStatusUpdated { .. } => {
Some("report_status_updated".to_string())
}
NotificationBody::ReportSubmitted { .. } => {
Some("report_submitted".to_string())
}
NotificationBody::ProjectStatusApproved { .. } => {
Some("project_status_approved".to_string())
}
NotificationBody::ProjectStatusNeutral { .. } => {
Some("project_status_neutral".to_string())
}
NotificationBody::ProjectTransferred { .. } => {
Some("project_transferred".to_string())
}
NotificationBody::ResetPassword { .. } => {
Some("reset_password".to_string())
}
@@ -147,6 +198,9 @@ impl LegacyNotification {
NotificationBody::PaymentFailed { .. } => {
Some("payment_failed".to_string())
}
NotificationBody::PayoutAvailable { .. } => {
Some("payout_available".to_string())
}
NotificationBody::LegacyMarkdown {
notification_type, ..
} => notification_type.clone(),
@@ -203,6 +257,46 @@ impl LegacyNotification {
project_id,
report_id,
},
NotificationBody::PatCreated { token_name } => {
LegacyNotificationBody::PatCreated { token_name }
}
NotificationBody::ModerationMessageReceived { project_id } => {
LegacyNotificationBody::ModerationMessageReceived { project_id }
}
NotificationBody::ReportStatusUpdated { report_id } => {
LegacyNotificationBody::ReportStatusUpdated { report_id }
}
NotificationBody::ReportSubmitted { report_id } => {
LegacyNotificationBody::ReportSubmitted { report_id }
}
NotificationBody::ProjectStatusApproved { project_id } => {
LegacyNotificationBody::ProjectStatusApproved { project_id }
}
NotificationBody::ProjectStatusNeutral {
project_id,
old_status,
new_status,
} => LegacyNotificationBody::ProjectStatusNeutral {
project_id,
old_status,
new_status,
},
NotificationBody::ProjectTransferred {
project_id,
new_owner_user_id,
new_owner_organization_id,
} => LegacyNotificationBody::ProjectTransferred {
project_id,
new_owner_user_id,
new_owner_organization_id,
},
NotificationBody::PayoutAvailable {
amount,
date_available,
} => LegacyNotificationBody::PayoutAvailable {
amount,
date_available,
},
NotificationBody::LegacyMarkdown {
notification_type,
name,

View File

@@ -46,6 +46,14 @@ pub enum NotificationType {
PasswordRemoved,
EmailChanged,
PaymentFailed,
PatCreated,
ModerationMessageReceived,
ReportStatusUpdated,
ReportSubmitted,
ProjectStatusApproved,
ProjectStatusNeutral,
ProjectTransferred,
PayoutAvailable,
Unknown,
}
@@ -68,6 +76,18 @@ impl NotificationType {
NotificationType::PasswordRemoved => "password_removed",
NotificationType::EmailChanged => "email_changed",
NotificationType::PaymentFailed => "payment_failed",
NotificationType::PatCreated => "pat_created",
NotificationType::ModerationMessageReceived => {
"moderation_message_received"
}
NotificationType::ReportStatusUpdated => "report_status_updated",
NotificationType::ReportSubmitted => "report_submitted",
NotificationType::ProjectStatusApproved => {
"project_status_approved"
}
NotificationType::ProjectStatusNeutral => "project_status_neutral",
NotificationType::ProjectTransferred => "project_transferred",
NotificationType::PayoutAvailable => "payout_available",
NotificationType::Unknown => "unknown",
}
}
@@ -90,6 +110,18 @@ impl NotificationType {
"password_removed" => NotificationType::PasswordRemoved,
"email_changed" => NotificationType::EmailChanged,
"payment_failed" => NotificationType::PaymentFailed,
"pat_created" => NotificationType::PatCreated,
"moderation_message_received" => {
NotificationType::ModerationMessageReceived
}
"report_status_updated" => NotificationType::ReportStatusUpdated,
"report_submitted" => NotificationType::ReportSubmitted,
"project_status_approved" => {
NotificationType::ProjectStatusApproved
}
"project_status_neutral" => NotificationType::ProjectStatusNeutral,
"project_transferred" => NotificationType::ProjectTransferred,
"payout_available" => NotificationType::PayoutAvailable,
"unknown" => NotificationType::Unknown,
_ => NotificationType::Unknown,
}
@@ -120,6 +152,7 @@ pub enum NotificationBody {
old_status: ProjectStatus,
new_status: ProjectStatus,
},
/// This is for website notifications only. Email notifications have `ModerationMessageReceived`.
ModeratorMessage {
thread_id: ThreadId,
message_id: ThreadMessageId,
@@ -127,6 +160,33 @@ pub enum NotificationBody {
project_id: Option<ProjectId>,
report_id: Option<ReportId>,
},
PatCreated {
token_name: String,
},
/// This differs from ModeratorMessage as this notification is only for project threads and
/// email notifications, not for site notifications.
ModerationMessageReceived {
project_id: ProjectId,
},
ReportStatusUpdated {
report_id: ReportId,
},
ReportSubmitted {
report_id: ReportId,
},
ProjectStatusApproved {
project_id: ProjectId,
},
ProjectStatusNeutral {
project_id: ProjectId,
old_status: ProjectStatus,
new_status: ProjectStatus,
},
ProjectTransferred {
project_id: ProjectId,
new_owner_user_id: Option<UserId>,
new_owner_organization_id: Option<OrganizationId>,
},
LegacyMarkdown {
notification_type: Option<String>,
name: String,
@@ -158,6 +218,10 @@ pub enum NotificationBody {
amount: String,
service: String,
},
PayoutAvailable {
date_available: DateTime<Utc>,
amount: f64,
},
Unknown,
}
@@ -177,6 +241,25 @@ impl NotificationBody {
NotificationBody::ModeratorMessage { .. } => {
NotificationType::ModeratorMessage
}
NotificationBody::PatCreated { .. } => NotificationType::PatCreated,
NotificationBody::ModerationMessageReceived { .. } => {
NotificationType::ModerationMessageReceived
}
NotificationBody::ReportStatusUpdated { .. } => {
NotificationType::ReportStatusUpdated
}
NotificationBody::ReportSubmitted { .. } => {
NotificationType::ReportSubmitted
}
NotificationBody::ProjectStatusApproved { .. } => {
NotificationType::ProjectStatusApproved
}
NotificationBody::ProjectStatusNeutral { .. } => {
NotificationType::ProjectStatusNeutral
}
NotificationBody::ProjectTransferred { .. } => {
NotificationType::ProjectTransferred
}
NotificationBody::LegacyMarkdown { .. } => {
NotificationType::LegacyMarkdown
}
@@ -210,6 +293,9 @@ impl NotificationBody {
NotificationBody::PaymentFailed { .. } => {
NotificationType::PaymentFailed
}
NotificationBody::PayoutAvailable { .. } => {
NotificationType::PayoutAvailable
}
NotificationBody::Unknown => NotificationType::Unknown,
}
}
@@ -323,6 +409,46 @@ impl From<DBNotification> for Notification {
},
vec![],
),
// The notifications from here to down below are listed with messages for completeness' sake,
// though they should never be sent via site notifications. This should be disabled via database
// options. Messages should be reviewed and worded better if we want to distribute these notifications
// via the site.
NotificationBody::PatCreated { token_name } => (
"New personal access token created".to_string(),
format!("Your personal access token '{token_name}' was created."),
"#".to_string(),
vec![],
),
NotificationBody::ReportStatusUpdated { .. } => (
"Report status updated".to_string(),
"A report you are involved in has been updated.".to_string(),
"#".to_string(),
vec![],
),
NotificationBody::ReportSubmitted { .. } => (
"Report submitted".to_string(),
"Your report was submitted successfully.".to_string(),
"#".to_string(),
vec![],
),
NotificationBody::ProjectStatusApproved { .. } => (
"Project approved".to_string(),
"Your project has been approved.".to_string(),
"#".to_string(),
vec![],
),
NotificationBody::ProjectStatusNeutral { .. } => (
"Project status updated".to_string(),
"Your project status has been updated.".to_string(),
"#".to_string(),
vec![],
),
NotificationBody::ProjectTransferred { .. } => (
"Project ownership transferred".to_string(),
"A project's ownership has been transferred.".to_string(),
"#".to_string(),
vec![],
),
// Don't expose the `flow` field
NotificationBody::ResetPassword { .. } => (
"Password reset requested".to_string(),
@@ -342,10 +468,6 @@ impl From<DBNotification> for Notification {
link.clone(),
actions.clone().into_iter().collect(),
),
// The notifications from here to down below are listed with messages for completeness' sake,
// though they should never be sent via site notifications. This should be disabled via database
// options. Messages should be reviewed and worded better if we want to distribute these notifications
// via the site.
NotificationBody::PaymentFailed { .. } => (
"Payment failed".to_string(),
"A payment on your account failed. Please update your billing information.".to_string(),
@@ -400,6 +522,18 @@ impl From<DBNotification> for Notification {
"#".to_string(),
vec![],
),
NotificationBody::PayoutAvailable { .. } => (
"Payout available".to_string(),
"A payout is available!".to_string(),
"#".to_string(),
vec![],
),
NotificationBody::ModerationMessageReceived { .. } => (
"New message in moderation thread".to_string(),
"You have a new message in a moderation thread.".to_string(),
"#".to_string(),
vec![],
),
NotificationBody::Unknown => {
("".to_string(), "".to_string(), "#".to_string(), vec![])
}