use crate::state::ChannelId;
use bytes::Bytes;
use helix_core::effect::DomainEventBytes;
use helix_core::Effect;
pub(super) fn emit(event: &'static str, data: serde_json::Value) -> Effect {
use serde_json::json;
let payload = json!({ "event": event, "data": data });
let bytes = Bytes::from(
serde_json::to_vec(&payload)
.expect("emit_s1: static JSON shape must not fail to serialize"),
);
Effect::Emit {
event: DomainEventBytes(bytes),
}
}
fn first_i64(data: &serde_json::Value, keys: &[&str]) -> Option<i64> {
keys.iter().find_map(|key| match data.get(*key) {
Some(serde_json::Value::Number(value)) => value.as_i64(),
Some(serde_json::Value::String(value)) => value.parse::<i64>().ok(),
_ => None,
})
}
fn first_bool(data: &serde_json::Value, keys: &[&str]) -> Option<bool> {
keys.iter().find_map(|key| match data.get(*key) {
Some(serde_json::Value::Bool(value)) => Some(*value),
Some(serde_json::Value::Number(value)) => value.as_i64().map(|value| value != 0),
Some(serde_json::Value::String(value)) if value == "1" => Some(true),
Some(serde_json::Value::String(value)) if value == "0" => Some(false),
Some(serde_json::Value::String(value)) if value.eq_ignore_ascii_case("true") => Some(true),
Some(serde_json::Value::String(value)) if value.eq_ignore_ascii_case("false") => {
Some(false)
}
_ => None,
})
}
pub fn emit_channel_created(channel_id: ChannelId, raw: &[u8]) -> Effect {
let channel: serde_json::Value = serde_json::from_slice(raw).unwrap_or(serde_json::Value::Null);
let display_name = channel
.get("displayName")
.and_then(serde_json::Value::as_str)
.filter(|value| !value.is_empty())
.unwrap_or(channel_id.as_str());
let channel_type = crate::channel_kind::channel_type(&channel);
let channel_kind = crate::channel_kind::channel_kind(channel_type);
let member_count = channel
.get("memberCount")
.and_then(serde_json::Value::as_i64)
.unwrap_or(0);
let created_at = first_i64(&channel, &["createdAt", "createAt", "created_at"]).unwrap_or(0);
let last_activity_at =
first_i64(&channel, &["lastPostAt", "last_post_at"]).unwrap_or(created_at);
let top = first_bool(&channel, &["channelIsTop", "isTop", "is_top"]).unwrap_or(false);
emit(
"im:channel:created",
serde_json::json!({
"id": channel_id.as_str(),
"channelId": channel_id.as_str(),
"channel_id": channel_id.as_str(),
"displayName": display_name,
"group": channel_kind,
"channelType": channel_type,
"channelKind": channel_kind,
"memberCount": member_count,
"createdAt": created_at,
"lastActivityAt": last_activity_at,
"top": top,
"projectionSource": "channel_created",
"props": channel,
"channel": channel,
}),
)
}
pub fn emit_channel_closed(channel_id: ChannelId, delete_at: i64) -> Effect {
emit(
"im:channel:closed",
serde_json::json!({
"id": channel_id.as_str(),
"channelId": channel_id.as_str(),
"closed": true,
"deleteAt": delete_at,
}),
)
}
pub fn emit_channel_left(channel_id: ChannelId) -> Effect {
emit(
"im:channel:closed",
serde_json::json!({
"id": channel_id.as_str(),
"channelId": channel_id.as_str(),
"terminalReason": "left",
}),
)
}
pub fn emit_channel_member_updated(channel_id: ChannelId, raw: &[u8]) -> Effect {
let channel: serde_json::Value = serde_json::from_slice(raw).unwrap_or(serde_json::Value::Null);
emit(
"im:channel:member-updated",
serde_json::json!({ "channel_id": channel_id.as_str(), "channel": channel }),
)
}
pub fn emit_channel_settings_updated(
channel_id: ChannelId,
channel: &serde_json::Value,
setting_version: u64,
) -> Option<Effect> {
let mut settings = serde_json::Map::new();
for key in [
"displayName",
"header",
"purpose",
"orient",
"module",
"source",
"pictureType",
"picture",
"notice",
] {
if let Some(value) = channel.get(key) {
settings.insert(key.to_string(), value.clone());
}
}
if settings.is_empty() {
return None;
}
Some(emit(
"im:channel:settings-updated",
serde_json::json!({
"channelId": channel_id.as_str(),
"settings": settings,
"settingVersion": setting_version,
}),
))
}
pub fn emit_member_nickname(channel_id: ChannelId, user_id: &str, nick_name: &str) -> Effect {
emit(
"im:channel:member-nickname",
serde_json::json!({
"channelId": channel_id.as_str(),
"userId": user_id,
"nickName": nick_name,
}),
)
}
mod storage;
pub use storage::{
channel_set_cols, channel_set_cols_op, delete_channel_members, delete_channel_members_op,
update_member_nickname, update_member_nickname_op, upsert_channel_members,
upsert_channel_members_op, MemberRow,
};