821537186e
- Reorganized import statements in adapter tests for better readability - Replaced or_insert_with(Vec::new) with or_default() in test closures - Updated Cargo.lock with new dependency versions and checksums - Added TLS features to tonic dependency configuration - Included sqlx, chrono, and uuid dependencies with specific features - Added jsonwebtoken and arc-swap as project dependencies - Reformatted assertion statements to comply with line length limits - Adjusted base64 import order in engine codec module - Updated protobuf include statement formatting
98 lines
2.8 KiB
Rust
98 lines
2.8 KiB
Rust
//! Notification delivery tracking — maps to `message_notification` table.
|
|
//!
|
|
//! Records when a message triggers a notification for a user (mention, reply,
|
|
//! thread activity, etc.) and tracks the delivery/read lifecycle.
|
|
//! Separate from `message_mention` which only covers @mentions.
|
|
|
|
use chrono::{DateTime, Utc};
|
|
use serde::{Deserialize, Serialize};
|
|
use uuid::Uuid;
|
|
|
|
/// A notification triggered for a user by a message.
|
|
///
|
|
/// Maps to the `message_notification` table. Records when a message triggers
|
|
/// a notification (mention, reply, thread activity) and tracks delivery/read.
|
|
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
|
|
pub struct MessageNotification {
|
|
pub id: Uuid,
|
|
pub message_id: Uuid,
|
|
pub channel_id: Uuid,
|
|
pub user_id: Uuid,
|
|
/// "mention" | "reply" | "thread" | "watch"
|
|
pub reason: String,
|
|
/// "pending" | "delivered" | "read" | "dismissed"
|
|
pub status: String,
|
|
/// Channel of delivery: "push" | "email" | "in_app"
|
|
pub delivery_channel: Option<String>,
|
|
pub delivered_at: Option<DateTime<Utc>>,
|
|
pub read_at: Option<DateTime<Utc>>,
|
|
pub created_at: DateTime<Utc>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum NotificationReason {
|
|
#[default]
|
|
Mention,
|
|
Reply,
|
|
Thread,
|
|
Watch,
|
|
}
|
|
|
|
impl NotificationReason {
|
|
pub fn as_str(&self) -> &'static str {
|
|
match self {
|
|
Self::Mention => "mention",
|
|
Self::Reply => "reply",
|
|
Self::Thread => "thread",
|
|
Self::Watch => "watch",
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum NotificationStatus {
|
|
#[default]
|
|
Pending,
|
|
Delivered,
|
|
Read,
|
|
Dismissed,
|
|
}
|
|
|
|
impl NotificationStatus {
|
|
pub fn as_str(&self) -> &'static str {
|
|
match self {
|
|
Self::Pending => "pending",
|
|
Self::Delivered => "delivered",
|
|
Self::Read => "read",
|
|
Self::Dismissed => "dismissed",
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_notification_serialize() {
|
|
let n = MessageNotification {
|
|
id: Uuid::now_v7(),
|
|
message_id: Uuid::now_v7(),
|
|
channel_id: Uuid::now_v7(),
|
|
user_id: Uuid::now_v7(),
|
|
reason: NotificationReason::Mention.as_str().into(),
|
|
status: NotificationStatus::Pending.as_str().into(),
|
|
delivery_channel: Some("push".into()),
|
|
delivered_at: None,
|
|
read_at: None,
|
|
created_at: Utc::now(),
|
|
};
|
|
|
|
let json = serde_json::to_value(&n).unwrap();
|
|
assert_eq!(json["reason"], "mention");
|
|
assert_eq!(json["status"], "pending");
|
|
}
|
|
}
|