mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 21:45:31 +09:00
feat(threads): gateway, messages and push thread support (#3250)
This commit is contained in:
1 parent
4e730832c7
commit
5f4295e399
108 files changed
+15857
-176
No files matched your search
+114
-3
@@ -26,10 +26,17 @@ const RING_CALLER_NAME_KEY: &str = "caller_name";
|
||||
const RING_CALLER_AVATAR_KEY: &str = "caller_avatar_url";
|
||||
const RING_AVATAR_KEYS: [&str; 1] = [RING_CALLER_AVATAR_KEY];
|
||||
const RING_IDENTITY_KEYS: [&str; 2] = [RING_CALLER_ID_KEY, RING_CALLER_NAME_KEY];
|
||||
const MINIMAL_DATA_KEYS: [&str; 9] = [
|
||||
const THREAD_DATA_KEYS: [&str; 3] = ["parent_id", "parent_name", "channel_name"];
|
||||
const NOTIF_TYPE_KEY: &str = "notif_type_id";
|
||||
const FORUM_THREAD_CREATED_NOTIF_TYPE: u64 = 9;
|
||||
const FCM_DEFAULT_CHANNEL_ID: &str = "fluxer_default_push";
|
||||
const FCM_FORUM_THREAD_CREATED_CHANNEL_ID: &str = "fluxer_forum_thread_created";
|
||||
const MINIMAL_DATA_KEYS: [&str; 11] = [
|
||||
"type",
|
||||
"action",
|
||||
"channel_id",
|
||||
"parent_id",
|
||||
NOTIF_TYPE_KEY,
|
||||
"message_id",
|
||||
"guild_id",
|
||||
"target_user_id",
|
||||
@@ -46,7 +53,7 @@ pub fn web_push_message(job: &MessageJob, target_user_id: &str, badge_count: u32
|
||||
} else {
|
||||
Value::String(job.guild_id.clone())
|
||||
};
|
||||
let data = json!({
|
||||
let mut data = json!({
|
||||
"channel_id": job.channel_id,
|
||||
"author_avatar_url": fields.icon,
|
||||
"message_id": job.message_id,
|
||||
@@ -57,6 +64,7 @@ pub fn web_push_message(job: &MessageJob, target_user_id: &str, badge_count: u32
|
||||
"target_user_id": target_user_id,
|
||||
"has_media": image_url.is_some(),
|
||||
});
|
||||
merge_thread_fields(&mut data, job);
|
||||
let notification = json!({
|
||||
"title": fields.title,
|
||||
"body": fields.body,
|
||||
@@ -81,6 +89,28 @@ pub fn web_push_message(job: &MessageJob, target_user_id: &str, badge_count: u32
|
||||
envelope
|
||||
}
|
||||
|
||||
fn merge_thread_fields(data: &mut Value, job: &MessageJob) {
|
||||
let Some(object) = data.as_object_mut() else {
|
||||
return;
|
||||
};
|
||||
let fields = [
|
||||
job.parent_id.as_deref(),
|
||||
job.parent_name.as_deref(),
|
||||
job.channel_name.as_deref(),
|
||||
];
|
||||
for (key, value) in THREAD_DATA_KEYS.into_iter().zip(fields) {
|
||||
if let Some(value) = value.filter(|value| !value.is_empty()) {
|
||||
object.insert(key.to_owned(), value.into());
|
||||
}
|
||||
}
|
||||
if job.is_forum_thread_created() {
|
||||
object.insert(
|
||||
NOTIF_TYPE_KEY.to_owned(),
|
||||
FORUM_THREAD_CREATED_NOTIF_TYPE.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn web_push_clear(job: &ClearJob, badge_count: u32) -> Value {
|
||||
let tag = format!("channel:{}", job.channel_id);
|
||||
let data = json!({
|
||||
@@ -200,7 +230,7 @@ fn fcm_notification_message(device_token: &str, envelope: &Value) -> Value {
|
||||
data.insert("image_url".to_owned(), url.into());
|
||||
}
|
||||
let mut android_notification = json!({
|
||||
"channel_id": "fluxer_default_push",
|
||||
"channel_id": fcm_channel_id(envelope),
|
||||
"tag": tag,
|
||||
"click_action": FCM_CLICK_ACTION,
|
||||
});
|
||||
@@ -220,6 +250,18 @@ fn fcm_notification_message(device_token: &str, envelope: &Value) -> Value {
|
||||
})
|
||||
}
|
||||
|
||||
fn fcm_channel_id(envelope: &Value) -> &'static str {
|
||||
let notif_type = envelope
|
||||
.get("data")
|
||||
.and_then(|data| data.get(NOTIF_TYPE_KEY))
|
||||
.and_then(Value::as_u64);
|
||||
if notif_type == Some(FORUM_THREAD_CREATED_NOTIF_TYPE) {
|
||||
FCM_FORUM_THREAD_CREATED_CHANNEL_ID
|
||||
} else {
|
||||
FCM_DEFAULT_CHANNEL_ID
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apns_payload(envelope: &Value) -> Value {
|
||||
let data = envelope.get("data");
|
||||
let mut payload = data.and_then(Value::as_object).cloned().unwrap_or_default();
|
||||
@@ -616,6 +658,21 @@ mod tests {
|
||||
image_url: image_url.map(str::to_owned),
|
||||
},
|
||||
user_ids: vec![USER_ID.to_owned()],
|
||||
kind: None,
|
||||
parent_id: None,
|
||||
parent_name: None,
|
||||
channel_name: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn thread_job() -> MessageJob {
|
||||
MessageJob {
|
||||
guild_id: "4455".to_owned(),
|
||||
kind: Some("thread".to_owned()),
|
||||
parent_id: Some("7788".to_owned()),
|
||||
parent_name: Some("general".to_owned()),
|
||||
channel_name: Some("release plans".to_owned()),
|
||||
..message_job(None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -669,6 +726,60 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_message_outside_a_thread_carries_no_thread_keys() {
|
||||
let envelope = web_push_message(&message_job(None), USER_ID, 3);
|
||||
for key in THREAD_DATA_KEYS {
|
||||
assert!(envelope["data"].get(key).is_none(), "{key} leaked");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_thread_message_carries_its_parent_and_names() {
|
||||
let envelope = web_push_message(&thread_job(), USER_ID, 3);
|
||||
assert_eq!(envelope["data"]["parent_id"], json!("7788"));
|
||||
assert_eq!(envelope["data"]["parent_name"], json!("general"));
|
||||
assert_eq!(envelope["data"]["channel_name"], json!("release plans"));
|
||||
assert_eq!(apns_payload(&envelope)["parent_id"], json!("7788"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_a_forum_thread_created_message_carries_its_notification_type() {
|
||||
let thread = web_push_message(&thread_job(), USER_ID, 3);
|
||||
assert!(thread["data"].get(NOTIF_TYPE_KEY).is_none());
|
||||
let forum = MessageJob {
|
||||
kind: Some("forum_thread_created".to_owned()),
|
||||
..thread_job()
|
||||
};
|
||||
let envelope = web_push_message(&forum, USER_ID, 3);
|
||||
assert_eq!(envelope["data"][NOTIF_TYPE_KEY], json!(9));
|
||||
assert_eq!(envelope["data"]["parent_id"], json!("7788"));
|
||||
let message = fcm_message("device-token", &envelope);
|
||||
assert_eq!(message["message"]["data"][NOTIF_TYPE_KEY], json!("9"));
|
||||
assert_eq!(
|
||||
message["message"]["android"]["notification"]["channel_id"],
|
||||
json!(FCM_FORUM_THREAD_CREATED_CHANNEL_ID)
|
||||
);
|
||||
assert_eq!(
|
||||
fcm_message("device-token", &thread)["message"]["android"]["notification"]["channel_id"],
|
||||
json!(FCM_DEFAULT_CHANNEL_ID)
|
||||
);
|
||||
let budget = serialize(&minimal(&envelope, usize::MAX)).len();
|
||||
let (bytes, _step) = fit(&envelope, budget);
|
||||
let shrunk: Value = serde_json::from_slice(&bytes).expect("the shrunk payload is json");
|
||||
assert_eq!(shrunk["data"][NOTIF_TYPE_KEY], json!(9));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_shrunk_thread_message_keeps_its_parent_id() {
|
||||
let envelope = web_push_message(&thread_job(), USER_ID, 3);
|
||||
let budget = serialize(&minimal(&envelope, usize::MAX)).len();
|
||||
let (bytes, step) = fit(&envelope, budget);
|
||||
assert_eq!(step, Some(PayloadShrink::Minimal));
|
||||
let shrunk: Value = serde_json::from_slice(&bytes).expect("the shrunk payload is json");
|
||||
assert_eq!(shrunk["data"]["parent_id"], json!("7788"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_shrunk_clear_still_tells_the_client_it_is_a_clear() {
|
||||
let envelope = web_push_clear(&clear_job(), 3);
|
||||
|
||||
Reference in new issue
Block a user